Gwen claims certain attributes are not relevant. See the spreadsheet she attached on Moodle. These attributes WILL NOT be filled in.

Data Pre-Processing

library(corrplot)
corrplot 0.92 loaded
library(ggplot2)
library(tidyverse)
Registered S3 methods overwritten by 'dbplyr':
  method         from
  print.tbl_lazy     
  print.tbl_sql      
── Attaching packages ─────────────────────────────────────────────────────── tidyverse 1.3.2 ──✔ tibble  3.1.8      ✔ dplyr   1.0.10
✔ tidyr   1.2.1      ✔ stringr 1.4.1 
✔ readr   2.1.2      ✔ forcats 0.5.2 
✔ purrr   0.3.4      ── Conflicts ────────────────────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()
library(fastDummies)
library(GGally)
Registered S3 method overwritten by 'GGally':
  method from   
  +.gg   ggplot2

Get rid of old stuff

rm(list=ls())
par(mfrow=c(1,1))
# simple R program to read csv file using read.table()
raw_data <- read.csv2("./LCdata.csv", header = TRUE, row.names=NULL, sep=";")


head(raw_data)
summary(raw_data)
       id             member_id          loan_amnt      funded_amnt    funded_amnt_inv   
 Min.   :   54734   Min.   :   70473   Min.   :  500   Min.   :  500   Length:798641     
 1st Qu.: 9207230   1st Qu.:10877939   1st Qu.: 8000   1st Qu.: 8000   Class :character  
 Median :34433372   Median :37095300   Median :13000   Median :13000   Mode  :character  
 Mean   :32463636   Mean   :35000265   Mean   :14754   Mean   :14741                     
 3rd Qu.:54900100   3rd Qu.:58470266   3rd Qu.:20000   3rd Qu.:20000                     
 Max.   :68617057   Max.   :73544841   Max.   :35000   Max.   :35000                     
                                                                                         
     term             int_rate         installment         emp_title          emp_length       
 Length:798641      Length:798641      Length:798641      Length:798641      Length:798641     
 Class :character   Class :character   Class :character   Class :character   Class :character  
 Mode  :character   Mode  :character   Mode  :character   Mode  :character   Mode  :character  
                                                                                               
                                                                                               
                                                                                               
                                                                                               
 home_ownership      annual_inc        verification_status   issue_d         
 Length:798641      Length:798641      Length:798641       Length:798641     
 Class :character   Class :character   Class :character    Class :character  
 Mode  :character   Mode  :character   Mode  :character    Mode  :character  
                                                                             
                                                                             
                                                                             
                                                                             
 loan_status         pymnt_plan            url                desc             purpose         
 Length:798641      Length:798641      Length:798641      Length:798641      Length:798641     
 Class :character   Class :character   Class :character   Class :character   Class :character  
 Mode  :character   Mode  :character   Mode  :character   Mode  :character   Mode  :character  
                                                                                               
                                                                                               
                                                                                               
                                                                                               
    title             zip_code          addr_state            dti             delinq_2yrs     
 Length:798641      Length:798641      Length:798641      Length:798641      Min.   : 0.0000  
 Class :character   Class :character   Class :character   Class :character   1st Qu.: 0.0000  
 Mode  :character   Mode  :character   Mode  :character   Mode  :character   Median : 0.0000  
                                                                             Mean   : 0.3145  
                                                                             3rd Qu.: 0.0000  
                                                                             Max.   :39.0000  
                                                                             NA's   :25       
 earliest_cr_line   inq_last_6mths    mths_since_last_delinq mths_since_last_record
 Length:798641      Min.   : 0.0000   Min.   :  0.0          Min.   :  0.0         
 Class :character   1st Qu.: 0.0000   1st Qu.: 15.0          1st Qu.: 51.0         
 Mode  :character   Median : 0.0000   Median : 31.0          Median : 70.0         
                    Mean   : 0.6947   Mean   : 34.1          Mean   : 70.1         
                    3rd Qu.: 1.0000   3rd Qu.: 50.0          3rd Qu.: 92.0         
                    Max.   :33.0000   Max.   :188.0          Max.   :129.0         
                    NA's   :25        NA's   :408818         NA's   :675190        
    open_acc        pub_rec          revol_bal        revol_util          total_acc     
 Min.   : 0.00   Min.   : 0.0000   Min.   :      0   Length:798641      Min.   :  1.00  
 1st Qu.: 8.00   1st Qu.: 0.0000   1st Qu.:   6443   Class :character   1st Qu.: 17.00  
 Median :11.00   Median : 0.0000   Median :  11876   Mode  :character   Median : 24.00  
 Mean   :11.55   Mean   : 0.1953   Mean   :  16930                      Mean   : 25.27  
 3rd Qu.:14.00   3rd Qu.: 0.0000   3rd Qu.:  20839                      3rd Qu.: 32.00  
 Max.   :90.00   Max.   :63.0000   Max.   :2904836                      Max.   :169.00  
 NA's   :25      NA's   :25        NA's   :2                            NA's   :25      
 initial_list_status  out_prncp         out_prncp_inv      total_pymnt       
 Length:798641       Length:798641      Length:798641      Length:798641     
 Class :character    Class :character   Class :character   Class :character  
 Mode  :character    Mode  :character   Mode  :character   Mode  :character  
                                                                             
                                                                             
                                                                             
                                                                             
 total_pymnt_inv    total_rec_prncp    total_rec_int      total_rec_late_fee  recoveries       
 Length:798641      Length:798641      Length:798641      Length:798641      Length:798641     
 Class :character   Class :character   Class :character   Class :character   Class :character  
 Mode  :character   Mode  :character   Mode  :character   Mode  :character   Mode  :character  
                                                                                               
                                                                                               
                                                                                               
                                                                                               
 collection_recovery_fee last_pymnt_d       last_pymnt_amnt    next_pymnt_d      
 Length:798641           Length:798641      Length:798641      Length:798641     
 Class :character        Class :character   Class :character   Class :character  
 Mode  :character        Mode  :character   Mode  :character   Mode  :character  
                                                                                 
                                                                                 
                                                                                 
                                                                                 
 last_credit_pull_d collections_12_mths_ex_med mths_since_last_major_derog  policy_code
 Length:798641      Min.   : 0.00000           Min.   :  0.0               Min.   :1   
 Class :character   1st Qu.: 0.00000           1st Qu.: 27.0               1st Qu.:1   
 Mode  :character   Median : 0.00000           Median : 44.0               Median :1   
                    Mean   : 0.01447           Mean   : 44.1               Mean   :1   
                    3rd Qu.: 0.00000           3rd Qu.: 61.0               3rd Qu.:1   
                    Max.   :20.00000           Max.   :188.0               Max.   :1   
                    NA's   :126                NA's   :599107                          
 application_type   annual_inc_joint    dti_joint         verification_status_joint
 Length:798641      Length:798641      Length:798641      Length:798641            
 Class :character   Class :character   Class :character   Class :character         
 Mode  :character   Mode  :character   Mode  :character   Mode  :character         
                                                                                   
                                                                                   
                                                                                   
                                                                                   
 acc_now_delinq       tot_coll_amt      tot_cur_bal       open_acc_6m       open_il_6m    
 Min.   : 0.000000   Min.   :      0   Min.   :      0   Min.   : 0.0     Min.   : 0.0    
 1st Qu.: 0.000000   1st Qu.:      0   1st Qu.:  29861   1st Qu.: 0.0     1st Qu.: 1.0    
 Median : 0.000000   Median :      0   Median :  80647   Median : 1.0     Median : 2.0    
 Mean   : 0.005026   Mean   :    228   Mean   : 139508   Mean   : 1.1     Mean   : 2.9    
 3rd Qu.: 0.000000   3rd Qu.:      0   3rd Qu.: 208229   3rd Qu.: 2.0     3rd Qu.: 4.0    
 Max.   :14.000000   Max.   :9152545   Max.   :8000078   Max.   :14.0     Max.   :33.0    
 NA's   :25          NA's   :63276     NA's   :63276     NA's   :779525   NA's   :779525  
  open_il_12m      open_il_24m     mths_since_rcnt_il  total_bal_il      il_util         
 Min.   : 0.0     Min.   : 0.0     Min.   :  0.0      Min.   :     0   Length:798641     
 1st Qu.: 0.0     1st Qu.: 0.0     1st Qu.:  6.0      1st Qu.: 10164   Class :character  
 Median : 0.0     Median : 1.0     Median : 12.0      Median : 24545   Mode  :character  
 Mean   : 0.8     Mean   : 1.7     Mean   : 21.1      Mean   : 36429                     
 3rd Qu.: 1.0     3rd Qu.: 2.0     3rd Qu.: 23.0      3rd Qu.: 47640                     
 Max.   :12.0     Max.   :19.0     Max.   :363.0      Max.   :878459                     
 NA's   :779525   NA's   :779525   NA's   :780030     NA's   :779525                     
  open_rv_12m      open_rv_24m       max_bal_bc       all_util         total_rev_hi_lim 
 Min.   : 0.0     Min.   : 0       Min.   :    0    Length:798641      Min.   :      0  
 1st Qu.: 0.0     1st Qu.: 1       1st Qu.: 2406    Class :character   1st Qu.:  13900  
 Median : 1.0     Median : 2       Median : 4502    Mode  :character   Median :  23700  
 Mean   : 1.4     Mean   : 3       Mean   : 5878                       Mean   :  32093  
 3rd Qu.: 2.0     3rd Qu.: 4       3rd Qu.: 7774                       3rd Qu.:  39800  
 Max.   :22.0     Max.   :43       Max.   :83047                       Max.   :9999999  
 NA's   :779525   NA's   :779525   NA's   :779525                      NA's   :63276    
     inq_fi        total_cu_tl      inq_last_12m   
 Min.   : 0.0     Min.   : 0.0     Min.   :-4      
 1st Qu.: 0.0     1st Qu.: 0.0     1st Qu.: 0      
 Median : 0.0     Median : 0.0     Median : 2      
 Mean   : 0.9     Mean   : 1.5     Mean   : 2      
 3rd Qu.: 1.0     3rd Qu.: 2.0     3rd Qu.: 3      
 Max.   :16.0     Max.   :35.0     Max.   :32      
 NA's   :779525   NA's   :779525   NA's   :779525  
my_data <- raw_data

Start cleaning up the columns

id

A unique LC assigned ID for the loan listing.

Is: integer Should be: integer and non-zero

filter(my_data, is.na(my_data$id)) # all are non-Null
filter(my_data, id == 0) # all are non-zero
filter(my_data, id < 0) # all are non-zero

Is not relevant to decision-making and should be dropped.

member_id

A unique LC assigned Id for the borrower member.

is: integer should be: integer, non-Null, non-zero

filter(my_data, is.na(my_data$member_id)) # all are non-Null
filter(my_data, member_id == 0) # all are non-zero
filter(my_data, member_id < 0) # all are positive

This is probably not relevant, but it could be the case that a member has obtained prior loans, and then that could be very relevant for the interest rate on this loan.

This never actually happens. So better to drop it.

loan_amt

The listed amount of the loan applied for by the borrower. If at some point in time, the credit department reduces the loan amount, then it will be reflected in this value.

Is: integer Should be: integer, positive, non-zero, non-null

Data Exploration: 1. Histogram of the loan_amt in various binwidths

filter(my_data, is.na(my_data$loan_amnt)) # all are non-Null
filter(my_data, loan_amnt == 0) # all are non-zero
filter(my_data, loan_amnt < 0) # all are positive
summary(loan_amnt)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
    500    8000   13000   14754   20000   35000 
ggplot(data = my_data) +
  geom_histogram(aes(x = loan_amnt), binwidth = 100)

ggplot(data = my_data) +
  geom_histogram(aes(x = loan_amnt), binwidth = 250)

ggplot(data = my_data) +
  geom_histogram(aes(x = loan_amnt), binwidth = 500)

ggplot(data = my_data) +
  geom_histogram(aes(x = loan_amnt), binwidth = 1000)

funded_amnt

The total amount committed to that loan at that point in time.

is: integer should be: integer, non-null, non-negative

clear work done on this column

funded_amnt <-raw_data$funded_amnt

View the data


head(my_data$funded_amnt)
[1] 2600 6700 7000 3000 2525 5075
typeof(my_data$funded_amnt)
[1] "integer"
summary(my_data$funded_amnt)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
    500    8000   13000   14741   20000   35000 
filter(my_data, is.na(funded_amnt)) # no Null
filter(my_data, funded_amnt == 0) # no Zero
filter(my_data, funded_amnt < 0) # no negative

See where funded_amnt greater than, less than, equal to loan_amt

Visualize them together

ggplot(data = my_data) +
  geom_point(aes(x = loan_amnt, y = funded_amnt), alpha = 0.05)

funded_amnt_inv

The total amount committed by investors for that loan at that point in time. is: character should be: integer (or double?)

clear work done on this column

funded_amnt_inv <-raw_data$funded_amnt_inv

Turn them into integers

head(my_data$funded_amnt_inv)
[1] "575"  "6700" "3450" "125"  "225"  "350" 
my_data$funded_amnt_inv <- as.integer(my_data$funded_amnt_inv)
head(my_data$funded_amnt_inv)
[1]  575 6700 3450  125  225  350

See where funded_amnt_inv greater than, less than, equal to loan_amt

ggplot(data = my_data) +
  geom_point(aes(x = loan_amnt, y = funded_amnt_inv), alpha = 0.05)

This shows two different curves, indicating two different types of bowers receiving investor funding. What is the difference between the two types of borrowers?

term

The number of payments on the loan. Values are in months and can be either 36 or 60.

NOTE Gwen says this will not be available in new loan applications, but this makes no sense as there is a distinct difference in interest rates for 36 month versus 60 month loans.

is: character, either ” 36 months” or ” 60 months” should be: integer, either 36 or 60 (to represent number of months). But does it even matter if it’s essentially categorical?

This is similar to a categorical variable. Perhaps it explains the two curves observed in funded_amt_inv. (note, no, it does not appear to after analysis)

clear work done on this column

head(term)
[1] " 36 months" " 36 months" " 36 months" " 36 months" " 36 months" " 36 months"

Use this if keeping it as a string

my_data$term <- str_trim(my_data$term)
head(my_data$term)
[1] "36 months" "36 months" "36 months" "36 months" "36 months" "36 months"

visualizing the data

head(term)
[1] " 36 months" " 36 months" " 36 months" " 36 months" " 36 months" " 36 months"

There are about twice as many 36 month loans as 60 month.

Let’s plot the funded_amnt_inv filtering by 36 month and 60 month

This looks a lot like the other graph with no filtering.

This shows three curves. First, there is an upper curve where it begins linearly. Second, there is a discontinuity where the slope drastically changes towards 0 (and possibly appears to be upward curving?) Third, there is the lower curve, which appears more linear than those for 36 month terms, but still downward curving overall.

Visualize term versus loan amount

ggplot() + 
  geom_boxplot(mapping = aes(x = term, y = loan_amnt), data = filter(my_data, term == "60 months")) + 
  geom_boxplot(mapping = aes(x = term, y = loan_amnt), data = filter(my_data, term == "36 months"))

60 month loans are for more money than 36 month loans

ggplot() + 
  geom_boxplot(mapping = aes(x = term, y = funded_amnt), data = filter(my_data, term == "36 months")) + 
  geom_boxplot(mapping = aes(x = term, y = funded_amnt), data = filter(my_data, term == "60 months"))

The below provides a worthless graph. Nearly all loans fund regardless of term.

ggplot() + 
  geom_boxplot(mapping = aes(x = term, y = funded_amnt / loan_amnt), data = filter(my_data, term == "36 months")) + 
  geom_boxplot(mapping = aes(x = term, y = funded_amnt / loan_amnt), data = filter(my_data, term == "60 months"))

int_rate

Interest Rate on the loan

is: character should be: float

head(my_data$int_rate)
[1] "8.38"  "7.75"  "7.75"  "9.01"  "9.33"  "10.28"
my_data$int_rate <- as.double(my_data$int_rate)
head(my_data$int_rate)
[1]  8.38  7.75  7.75  9.01  9.33 10.28

ggplot(my_data) +
  geom_histogram(aes(x = int_rate), binwidth = .1)

ggplot(my_data) +
  geom_histogram(aes(x = int_rate), binwidth = .25)

ggplot(my_data) +
  geom_histogram(aes(x = int_rate), binwidth = .5)

ggplot(my_data) +
  geom_histogram(aes(x = int_rate), binwidth = 1)

Interest rate versus loan amount 60 month loans have a higher starting interest rate, and there are very few under 10,000 loan amount

ggplot(data = my_data, mapping = aes(x = loan_amnt, y = int_rate, color = term)) +
    geom_point(alpha = .05)

installment

The monthly payment owed by the borrower if the loan originates.

Note Gwen says this will not be available in the new data.

This should be directly correlated with the loan amount and interest rate and determined by term. It may be useful to use these values instead of those when say, comparing to income, in order to avoid calculation.

Is: string Should be: double

head(my_data$installment)
[1] "81.94"  "209.18" "218.55" "95.42"  "80.69"  "164.42"
my_data$installment <- as.double(my_data$installment)
head(my_data$installment)
[1]  81.94 209.18 218.55  95.42  80.69 164.42

emp_title

The job title supplied by the Borrower when applying for the loan. Employer Title replaces Employer Name for all loans listed after 9/23/2013

This is a string entered by the user and should be dropped.

my_data <- subset(my_data, select = -emp_title)

emp_length

Employment length in years. Possible values are between 0 and 10 where 0 means less than one year and 10 means ten or more years.

Is: string Should be: Integer

head(my_data$emp_length)
[1] 3 0 0 0 0 0

Search NA

More than 40,000 rows have no employment information. That seems like a lot to drop. How should we handle them?

home_ownership

The home ownership status provided by the borrower during registration. Our values are: RENT, OWN, MORTGAGE, OTHER.

Is: String Should be: Perhaps also an ordered set of integers (“OTHER”, “RENT”, “MORTGAGE”, “OWN”). Could also make dummy columns, but this is my order if I were assessing borrowers. We should compare both models.

head(my_data$home_ownership)
[1] "MORTGAGE" "NONE"     "NONE"     "RENT"     "RENT"     "RENT"    
my_data$home_ownership <- as.integer(ordered(my_data$home_ownership, levels = c("OTHER", "RENT", "MORTGAGE", "OWN")))
head(my_data$home_ownership)
[1]  3 NA NA  2  2  2

Drop nulls. There are very few (47)

filter(my_data, is.na(my_data$home_ownership))
my_data <- filter(my_data, ! is.na(my_data$home_ownership))
filter(my_data, is.na(my_data$home_ownership))

annual_inc

The self-reported annual income provided by the borrower during registration.

Is: string should be: Int

Filter for null, zero, negative

filter(my_data, is.na(my_data$annual_inc)) # four items are non-Null, drop them.
filter(my_data, my_data$annual_inc == 0) # two are zero income, but significant income from joint applicant, so don't drop them.
filter(my_data, my_data$annual_inc < 10000) # 460 applicants.
filter(my_data, my_data$annual_inc < 500) # only the two 0 income applicants
filter(my_data, my_data$annual_inc < 0) # all are positive

Drop NA

my_data <- filter(my_data, ! is.na(annual_inc))
head(my_data$annual_inc)
[1]   6500  35000 110000  95000 150000  59000
ggplot(data = filter(my_data, annual_inc > 1000000), aes(x = annual_inc, y = int_rate)) +
         geom_point(aes(color = verification_status))

We should divide income over $1mil by 100 because presumably some of these people entered dollars and cents, but the system did not recognize that they were entering cents.

Needs a decision on the limit of income, where we will assume incorrect data entry. I assume $1,000,000 should have been $10,000.00, but it could be higher or lower.


my_data$annual_inc <- ifelse(my_data$annual_inc >= 1000000, my_data$annual_inc / 100, my_data$annual_inc)
filter(my_data, annual_inc > 1000000)


summary(my_data$annual_inc)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
      0   45000   64999   74559   90000  999999 

verification_status

is_inc_v in the dictionary Indicates if income was verified by LC, not verified, or if the income source was verified

make it an integer, with not verified at 0 in order to show the gain from verified

levels(my_data$verification_status)
NULL

no NA

Does verification have a benefit? Yes

lm.fit <- lm(int_rate~verification_status, my_data)
summary(lm.fit)

Call:
lm(formula = int_rate ~ verification_status, data = my_data)

Residuals:
    Min      1Q  Median      3Q     Max 
-9.2762 -3.2179 -0.2179  2.6738 17.1704 

Coefficients:
                     Estimate Std. Error t value Pr(>|t|)    
(Intercept)         11.819567   0.007769  1521.4   <2e-16 ***
verification_status  1.388327   0.005991   231.8   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 4.242 on 798592 degrees of freedom
Multiple R-squared:  0.06302,   Adjusted R-squared:  0.06302 
F-statistic: 5.371e+04 on 1 and 798592 DF,  p-value: < 2.2e-16
ggplot(data = filter(my_data, verification_status == 0), aes(x = verification_status, y = int_rate)) +
  geom_boxplot()

ggplot(data = filter(my_data, verification_status == 1), aes(x = verification_status, y = int_rate)) +
  geom_boxplot()

ggplot(data = filter(my_data, verification_status == 2), aes(x = verification_status, y = int_rate)) +
  geom_boxplot()

This is surprising that people with verified income have a higher interest rate than people with an unverified income.

issue_d

The month which the loan was funded

Note Gwen says this will not be in new data.

Is: string Should be:

Is this useful? Probably would imagine that interest rates change.

Added the date as 01 in order to convert to a date.

head(my_data$issue_d)
[1] "Jun-2007" "Jun-2007" "Jun-2007" "Jul-2007" "Jul-2007" "Jul-2013"
f <- my_data$issue_d

for (n in 1:length(f)){
  f[n] <- paste("01-", f[n], sep = "")
}
my_data$issue_d <- as.Date(f, format = "%d-%b-%Y")
head(my_data$issue_d)
[1] "2007-06-01" "2007-06-01" "2007-06-01" "2007-07-01" "2007-07-01" "2013-07-01"

See interest rate over time. I need to make a mean interest rate for each month, or maybe filter by term, to make this more helpful.

ggplot(my_data, aes(x = issue_d, y = int_rate)) +
  geom_line()

loan_status

Current status of the loan

This is for after the loan is issued, so I don’t think it will be useful in predicting interest rate.

head(my_data$loan_status)
[1] "Does not meet the credit policy. Status:Charged Off"
[2] "Does not meet the credit policy. Status:Fully Paid" 
[3] "Does not meet the credit policy. Status:Fully Paid" 
[4] "Does not meet the credit policy. Status:Fully Paid" 
[5] "Does not meet the credit policy. Status:Fully Paid" 
[6] "Charged Off"                                        

This applies only to current loans and should be deleted

my_data <- subset(my_data, select = -loan_status)

pymnt_plan

Indicates if a payment plan has been put in place for the loan

This applies only to current, defaulted loans and should be deleted

my_data <- subset(my_data, select = -pymnt_plan)

url

URL for the LC page with listing data.

This information is not relevant to analysis and should be dropped.

my_data <- subset(my_data, select = -url)

desc

Loan description provided by the borrower

This information could be important but would be impossible to analyse since it is user-entered.

my_data <- subset(my_data, select = -desc)

purpose

A category provided by the borrower for the loan request.

Is: String Should be: Dummy columns.

The answer here is relevant for the interest rate.

levels(factor(my_data$purpose))
 [1] "car"                "credit_card"        "debt_consolidation" "educational"       
 [5] "home_improvement"   "house"              "major_purchase"     "medical"           
 [9] "moving"             "other"              "renewable_energy"   "small_business"    
[13] "vacation"           "wedding"           
ggplot(my_data, aes(x = purpose, y = int_rate)) +
  geom_boxplot()

Should I remove the first dummy to guard against multicolliniarity?

Need to see whether each category has a good number of observations.

title

The loan title provided by the borrower

This information could be important but would be impossible to analyse since it is user-entered.

my_data <- subset(my_data, select = -title)

zip_code

The first 3 numbers of the zip code provided by the borrower in the loan application.

This information would indicate where in the country the borrower is located. It might be a relevant variable, but it would at best be an approximation of a borrower’s income. I do not think it provides significant information.

my_data <- subset(my_data, select = -zip_code)

addr_state

The state provided by the borrower in the loan application

This information would indicate where in the country the borrower is located. It might be a relevant variable, but it would at best be an approximation of a borrower’s income. I do not think it provides significant information.

my_data <- subset(my_data, select = -addr_state)

dti

A ratio calculated using the borrower’s total monthly debt payments on the total debt obligations, excluding mortgage and the requested LC loan, divided by the borrower’s self-reported monthly income.

Is: string Should be: double

filter(my_data, is.na(dti)) # no Null
filter(my_data, dti == 0) # 403 are zero, but this indicates no debt. Seems odd they turn to Lending Club for a loan if this is true.
filter(my_data, dti < 1)
filter(my_data, dti < 0) # no negative
head(my_data$dti)
[1] "6.46"  "10"    "10"    "10"    "5.6"   "24.55"
f <- as.double(my_data$dti)
head(f)
[1]  6.46 10.00 10.00 10.00  5.60 24.55

delinq_2yrs

The number of 30+ days past-due incidences of delinquency in the borrower’s credit file for the past 2 years

Is: Integer Should be: Integer

typeof(my_data$delinq_2yrs)
[1] "integer"
summary(my_data$delinq_2yrs)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    NA's 
 0.0000  0.0000  0.0000  0.3145  0.0000 39.0000      21 

There are 21 NAs. Drop them.

my_data <- filter(my_data, ! is.na(my_data$delinq_2yrs))
summary(my_data$delinq_2yrs)

earliest_cr_line

The month the borrower’s earliest reported credit line was opened

filter(raw_data, is.na(earliest_cr_line)) # no NA

This could be a placeholder for the borrower’s age. Is it correlated with interest rate? I would imagine that if this date is old, the person is old, so has a higher interest rate, and if young, low income, so higher interest rate. But this would simply be because of my expectations of income by age, and this data does not directly tell us anything.

Added the date as 01 in order to convert to a date.

head(my_data$earliest_cr_line)
[1] "Mar-1984" "Dec-1994" "Jul-1993" "Jan-1983" "Oct-1996" "Dec-1997"
f <- my_data$earliest_cr_line

for (n in 1:length(f)){
  f[n] <- paste("01-", f[n], sep = "")
}
my_data$earliest_cr_line <- as.Date(f, format = "%d-%b-%Y")
head(my_data$earliest_cr_line)
[1] "1984-03-01" "1994-12-01" "1993-07-01" "1983-01-01" "1996-10-01" "1997-12-01"

Drop the data as it is not really useful.

my_data <- subset(my_data, select = -earliest_cr_line)

inq_last_6mths

The number of inquiries in past 6 months (excluding auto and mortgage inquiries)

This represents how many times a lender has pulled the borrower’s credit report. In itself it does not mean anything, but a higher value is taken as an indication that the borrower is not creditworthy (else, why ask for so much debt?) 0-2 is fine.

There are no NA values

Plot the range

ggplot(data = my_data, aes(x = inq_last_6mths)) +
  geom_histogram(binwidth = 1)

Power law distribution.

summary(lm.1)

Call:
lm(formula = int_rate ~ inq_last_6mths, data = my_data)

Residuals:
     Min       1Q   Median       3Q      Max 
-31.3484  -3.3794  -0.2518   2.7606  16.4406 

Coefficients:
                Estimate Std. Error t value Pr(>|t|)    
(Intercept)    12.549391   0.005818  2157.0   <2e-16 ***
inq_last_6mths  1.001219   0.004785   209.2   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 4.267 on 798571 degrees of freedom
Multiple R-squared:  0.05197,   Adjusted R-squared:  0.05197 
F-statistic: 4.378e+04 on 1 and 798571 DF,  p-value: < 2.2e-16

This has a significant impact on interest rate.

mths_since_last_delinq

The number of months since the borrower’s last delinquency.

This is the time that has passed since a buyer was delinquent. Approximately half of the entries are NA. HOWEVER if the borrower has never been delinquent (that is, always pays on time), it SHOULD be NA. NA is a better value than having anything here. BUT the data shows this is not actually true, and those with NA have a higher interest rate.

Possible solutions: very, very high value to replace NA?

ggplot(data = my_data, aes(x = mths_since_last_delinq, y = int_rate)) +
  geom_point(alpha = 0.05)

There is a hard cutoff at 84 months because those delinquencies are no longer reported on a credit report (7 years). There are still some observations beyond that time period, but I am not sure why. They may pre-date the change in the law (I recall this was sometime around 2009-2010) or loopholes that allow continuing reporting, or self-reporting to LendingClub.

We need to figure out a way to deal with the <84, >=84 problem.

count(my_data, mths_since_last_delinq == 83)
count(my_data, mths_since_last_delinq == 84)
count(my_data, mths_since_last_delinq == 85)
NA

Those with an NA have a higher interest rate, which I would not expect.

ggplot(data = my_data) +
  geom_boxplot(mapping = aes(x = is.na(mths_since_last_delinq), y = int_rate))

mths_since_last_record

The number of months since the last public record.

This would be the months since the last public record filing, which means a lawsuit to collect a debt. From my old credit report from 2015: “Public record information includes bankruptcies, liens or judgments and comes from federal, state or county court records.” Today, they include only bankruptcies, but this is not relevant.

This is very, very bad, and much better if it is NA (i.e. there are none)

levels(factor(my_data$mths_since_last_record))
  [1] "0"   "1"   "2"   "3"   "4"   "5"   "6"   "7"   "8"   "9"   "10"  "11"  "12"  "13"  "14" 
 [16] "15"  "16"  "17"  "18"  "19"  "20"  "21"  "22"  "23"  "24"  "25"  "26"  "27"  "28"  "29" 
 [31] "30"  "31"  "32"  "33"  "34"  "35"  "36"  "37"  "38"  "39"  "40"  "41"  "42"  "43"  "44" 
 [46] "45"  "46"  "47"  "48"  "49"  "50"  "51"  "52"  "53"  "54"  "55"  "56"  "57"  "58"  "59" 
 [61] "60"  "61"  "62"  "63"  "64"  "65"  "66"  "67"  "68"  "69"  "70"  "71"  "72"  "73"  "74" 
 [76] "75"  "76"  "77"  "78"  "79"  "80"  "81"  "82"  "83"  "84"  "85"  "86"  "87"  "88"  "89" 
 [91] "90"  "91"  "92"  "93"  "94"  "95"  "96"  "97"  "98"  "99"  "100" "101" "102" "103" "104"
[106] "105" "106" "107" "108" "109" "110" "111" "112" "113" "114" "115" "116" "117" "118" "119"
[121] "120" "121" "129"
count(my_data, is.na(mths_since_last_record))
ggplot(data = my_data, aes(x = mths_since_last_record, y = int_rate)) +
  geom_point(alpha = 0.05)

I do not see a pattern appear.

They also remain on your credit report for ten years during this time period (this is no longer true, but not relevant)

count(my_data, mths_since_last_record == 119)
count(my_data, mths_since_last_record == 120)
NA

Thos with no public records have higher interest rates than those who do, which I would not expect.

ggplot(data = my_data) +
  geom_boxplot(mapping = aes(x = is.na(mths_since_last_record), y = int_rate))

open_acc

The number of open credit lines in the borrower’s credit file.

Is: Integer Should be: Integer

I would expect this to be generally on the high side for users of this platform.

typeof(my_data$open_acc)
[1] "integer"
levels(factor(my_data$open_acc))
 [1] "0"  "1"  "2"  "3"  "4"  "5"  "6"  "7"  "8"  "9"  "10" "11" "12" "13" "14" "15" "16" "17"
[19] "18" "19" "20" "21" "22" "23" "24" "25" "26" "27" "28" "29" "30" "31" "32" "33" "34" "35"
[37] "36" "37" "38" "39" "40" "41" "42" "43" "44" "45" "46" "47" "48" "49" "50" "51" "52" "53"
[55] "54" "55" "56" "57" "58" "59" "60" "61" "62" "63" "64" "65" "66" "67" "68" "75" "79" "82"
[73] "84" "90"
count(my_data, is.na(open_acc)) # no NA
ggplot(data = my_data, aes(x = open_acc)) +
  geom_histogram()


summary(my_data$open_acc)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
   0.00    8.00   11.00   11.55   14.00   90.00 

Is this associated with int_rate?

lm.2 <- lm(int_rate~open_acc, my_data)
summary(lm.2)

Call:
lm(formula = int_rate ~ open_acc, data = my_data)

Residuals:
   Min     1Q Median     3Q    Max 
-8.018 -3.277 -0.251  2.941 15.995 

Coefficients:
              Estimate Std. Error  t value Pr(>|t|)    
(Intercept) 13.3463730  0.0117312 1137.684   <2e-16 ***
open_acc    -0.0087796  0.0009227   -9.515   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 4.382 on 798571 degrees of freedom
Multiple R-squared:  0.0001134, Adjusted R-squared:  0.0001121 
F-statistic: 90.53 on 1 and 798571 DF,  p-value: < 2.2e-16

This says the interest rate goes down for people with more open accounts and that this is significant, which seems odd. The relationship is probably non-linear, if significant.

pub_rec

Number of derogatory public records

Is: Integer Should be: Integer

Histogram shows very, very strongly power law.

ggplot(data = my_data, aes(x = pub_rec)) +
  geom_histogram()

summary(my_data$pub_rec)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
 0.0000  0.0000  0.0000  0.1953  0.0000 63.0000 

revol_balance

Total credit revolving balance

This is the revolving debt (credit card, usually) of the borrower. It will be a part of the dti calculation.

Two are na. One has a revol_util figure, so I expect this is a data error. The other has a revol_util of 0, so this may be showing a revol_bal == 0. Since it is only two observations, we should drop them.

revol_util

Revolving line utilization rate, or the amount of credit the borrower is using relative to all available revolving credit.

This is a ratio showing how maxed out the borrower’s credit cards are. We would expect creditworthy borrowers to have a low ratio.

Is: string Should be: double

ggplot(data = my_data, aes(x = revol_util, y = int_rate)) +
  geom_point(alpha = 0.05)

Has a fairly normal distribution.

total_acc

The total number of credit lines currently in the borrower’s credit file

I do not expect this to be useful to the analysis. It does not distinguish between open or closed accounts. In general not a good predictor.

Is: integer Should be: integer

head(my_data$total_acc)
[1] 16 19 43 28 20 35

initial_list_status

The initial listing status of the loan. Possible values are – W, F

“Lending Club reserves a few loans for 12 hours and offers them to the institutional and large retail lenders who want to lend the whole amount for a loan. I am not sure whether the historical loan data file includes the loans that were offered and picked up by lenders as ‘whole’ loans. But, the loans that were initially offered as whole, designated with ‘w’, but not picked up as ‘whole’ loans are listed in historical loan data file.” https://andirog.blogspot.com/2013/04/lending-club-borrowers-income.html

This may be useful because those loans taken by institutional and large lenders may be the most preferred loans by lenders.

head(my_data$initial_list_status)
[1] "f" "f" "f" "f" "f" "f"

Is there a difference?

w loans tend to have a lower interest rate.

Make it an integer, with f == 0 and w == 1, but this could instead be dummy columns

head(my_data$initial_list_status)
[1] "f" "f" "f" "f" "f" "f"
my_data$initial_list_status <- as.integer(factor(my_data$initial_list_status)) - 1
head(my_data$initial_list_status)
[1] 0 0 0 0 0 0

Is this useful?

lm.3 <- lm(int_rate~initial_list_status, my_data)
summary(lm.3)

Call:
lm(formula = int_rate ~ initial_list_status, data = my_data)

Residuals:
    Min      1Q  Median      3Q     Max 
-8.4143 -3.5559 -0.2359  2.8557 16.2641 

Coefficients:
                     Estimate Std. Error t value Pr(>|t|)    
(Intercept)         13.734260   0.006789  2022.9   <2e-16 ***
initial_list_status -1.008355   0.009747  -103.5   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 4.353 on 798569 degrees of freedom
Multiple R-squared:  0.01323,   Adjusted R-squared:  0.01322 
F-statistic: 1.07e+04 on 1 and 798569 DF,  p-value: < 2.2e-16

out_prncp

Remaining outstanding principal for total amount funded

This is only relevant after a loan is issued and should be dropped.

my_data <- subset(my_data, select = -out_prncp)
Error in eval(substitute(select), nl, parent.frame()) : 
  object 'out_prncp' not found

out_prncp_inv

Remaining outstanding principal for portion of total amount funded by investors

This is only relevant after a loan is issued and should be dropped.

my_data <- subset(my_data, select = -out_prncp_inv)

total_pymnt

Payments received to date for total amount funded

This is only relevant after a loan is issued and should be dropped.

my_data <- subset(my_data, select = -total_pymnt)

total_pymnt_inv

Payments received to date for portion of total amount funded by investors

This is only relevant after a loan is issued and should be dropped.

my_data <- subset(my_data, select = -total_pymnt_inv)


head(my_data$dti_joint)
f <- as.double(my_data$dti_joint)
summary(f)
LS0tDQp0aXRsZTogIlIgTm90ZWJvb2siDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KR3dlbiBjbGFpbXMgY2VydGFpbiBhdHRyaWJ1dGVzIGFyZSBub3QgcmVsZXZhbnQuIFNlZSB0aGUgc3ByZWFkc2hlZXQgc2hlIGF0dGFjaGVkIG9uIE1vb2RsZS4NClRoZXNlIGF0dHJpYnV0ZXMgV0lMTCBOT1QgYmUgZmlsbGVkIGluLg0KDQojIERhdGEgUHJlLVByb2Nlc3NpbmcNCg0KYGBge3J9DQpsaWJyYXJ5KGNvcnJwbG90KQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KGZhc3REdW1taWVzKQ0KbGlicmFyeShHR2FsbHkpDQpgYGANCg0KR2V0IHJpZCBvZiBvbGQgc3R1ZmYNCmBgYHtyfQ0Kcm0obGlzdD1scygpKQ0KcGFyKG1mcm93PWMoMSwxKSkNCg0KYGBgDQoNCmBgYHtyfQ0KcmF3X2RhdGEgPC0gcmVhZC5jc3YyKCIuL0xDZGF0YS5jc3YiLCBoZWFkZXIgPSBUUlVFLCByb3cubmFtZXM9TlVMTCwgc2VwPSI7IikNCg0KDQpoZWFkKHJhd19kYXRhKQ0Kc3VtbWFyeShyYXdfZGF0YSkNCg0KbXlfZGF0YSA8LSByYXdfZGF0YQ0KYGBgDQoNClN0YXJ0IGNsZWFuaW5nIHVwIHRoZSBjb2x1bW5zDQoNCiMjIyBpZA0KQSB1bmlxdWUgTEMgYXNzaWduZWQgSUQgZm9yIHRoZSBsb2FuIGxpc3RpbmcuDQoNCklzOiBpbnRlZ2VyDQpTaG91bGQgYmU6IGludGVnZXIgYW5kIG5vbi16ZXJvDQpgYGB7cn0NCmZpbHRlcihteV9kYXRhLCBpcy5uYShteV9kYXRhJGlkKSkgIyBhbGwgYXJlIG5vbi1OdWxsDQpmaWx0ZXIobXlfZGF0YSwgaWQgPT0gMCkgIyBhbGwgYXJlIG5vbi16ZXJvDQpmaWx0ZXIobXlfZGF0YSwgaWQgPCAwKSAjIGFsbCBhcmUgcG9zaXRpdmUNCmBgYA0KDQpJcyBub3QgcmVsZXZhbnQgdG8gZGVjaXNpb24tbWFraW5nIGFuZCBzaG91bGQgYmUgZHJvcHBlZC4NCmBgYHtyfQ0KbXlfZGF0YSA8LSBzdWJzZXQobXlfZGF0YSwgc2VsZWN0ID0gLWlkKQ0KDQpgYGANCg0KIyMjIG1lbWJlcl9pZA0KQSB1bmlxdWUgTEMgYXNzaWduZWQgSWQgZm9yIHRoZSBib3Jyb3dlciBtZW1iZXIuDQoNCmlzOiBpbnRlZ2VyDQpzaG91bGQgYmU6IGludGVnZXIsIG5vbi1OdWxsLCBub24temVybw0KYGBge3J9DQpmaWx0ZXIobXlfZGF0YSwgaXMubmEobXlfZGF0YSRtZW1iZXJfaWQpKSAjIGFsbCBhcmUgbm9uLU51bGwNCmZpbHRlcihteV9kYXRhLCBtZW1iZXJfaWQgPT0gMCkgIyBhbGwgYXJlIG5vbi16ZXJvDQpmaWx0ZXIobXlfZGF0YSwgbWVtYmVyX2lkIDwgMCkgIyBhbGwgYXJlIHBvc2l0aXZlDQpgYGANCg0KVGhpcyBpcyBwcm9iYWJseSBub3QgcmVsZXZhbnQsIGJ1dCBpdCBjb3VsZCBiZSB0aGUgY2FzZSB0aGF0IGEgbWVtYmVyIGhhcyBvYnRhaW5lZCBwcmlvciBsb2FucywgYW5kIHRoZW4gdGhhdCBjb3VsZCBiZSB2ZXJ5IHJlbGV2YW50IGZvciB0aGUgaW50ZXJlc3QgcmF0ZSBvbiB0aGlzIGxvYW4uDQoNCmBgYHtyfQ0KZiA8LSBmaWx0ZXIobXlfZGF0YSwgZHVwbGljYXRlZChteV9kYXRhJG1lbWJlcl9pZCkgPT0gVFJVRSkNCmYNCmBgYA0KVGhpcyBuZXZlciBhY3R1YWxseSBoYXBwZW5zLiBTbyBiZXR0ZXIgdG8gZHJvcCBpdC4NCg0KYGBge3J9DQpteV9kYXRhIDwtIHN1YnNldChteV9kYXRhLCBzZWxlY3QgPSAtbWVtYmVyX2lkKQ0KDQpgYGANCg0KDQojIyMgbG9hbl9hbXQNClRoZSBsaXN0ZWQgYW1vdW50IG9mIHRoZSBsb2FuIGFwcGxpZWQgZm9yIGJ5IHRoZSBib3Jyb3dlci4gSWYgYXQgc29tZSBwb2ludCBpbiB0aW1lLCB0aGUgY3JlZGl0IGRlcGFydG1lbnQgcmVkdWNlcyB0aGUgbG9hbiBhbW91bnQsIHRoZW4gaXQgd2lsbCBiZSByZWZsZWN0ZWQgaW4gdGhpcyB2YWx1ZS4NCg0KSXM6IGludGVnZXINClNob3VsZCBiZTogaW50ZWdlciwgcG9zaXRpdmUsIG5vbi16ZXJvLCBub24tbnVsbA0KDQpEYXRhIEV4cGxvcmF0aW9uOg0KMS4gIEhpc3RvZ3JhbSBvZiB0aGUgbG9hbl9hbXQgaW4gdmFyaW91cyBiaW53aWR0aHMNCmBgYHtyfQ0KZmlsdGVyKG15X2RhdGEsIGlzLm5hKG15X2RhdGEkbG9hbl9hbW50KSkgIyBhbGwgYXJlIG5vbi1OdWxsDQpmaWx0ZXIobXlfZGF0YSwgbXlfZGF0YSRsb2FuX2FtbnQgPT0gMCkgIyBhbGwgYXJlIG5vbi16ZXJvDQpmaWx0ZXIobXlfZGF0YSwgbXlfZGF0YSRsb2FuX2FtbnQgPCAwKSAjIGFsbCBhcmUgcG9zaXRpdmUNCnN1bW1hcnkobXlfZGF0YSRsb2FuX2FtbnQpDQpnZ3Bsb3QoZGF0YSA9IG15X2RhdGEpICsNCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKHggPSBsb2FuX2FtbnQpLCBiaW53aWR0aCA9IDEwMCkNCmdncGxvdChkYXRhID0gbXlfZGF0YSkgKw0KICBnZW9tX2hpc3RvZ3JhbShhZXMoeCA9IGxvYW5fYW1udCksIGJpbndpZHRoID0gMjUwKQ0KZ2dwbG90KGRhdGEgPSBteV9kYXRhKSArDQogIGdlb21faGlzdG9ncmFtKGFlcyh4ID0gbG9hbl9hbW50KSwgYmlud2lkdGggPSA1MDApDQpnZ3Bsb3QoZGF0YSA9IG15X2RhdGEpICsNCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKHggPSBsb2FuX2FtbnQpLCBiaW53aWR0aCA9IDEwMDApDQpgYGANCg0KIyMjIGZ1bmRlZF9hbW50DQpUaGUgdG90YWwgYW1vdW50IGNvbW1pdHRlZCB0byB0aGF0IGxvYW4gYXQgdGhhdCBwb2ludCBpbiB0aW1lLg0KDQppczogaW50ZWdlcg0Kc2hvdWxkIGJlOiBpbnRlZ2VyLCBub24tbnVsbCwgbm9uLW5lZ2F0aXZlDQoNCmNsZWFyIHdvcmsgZG9uZSBvbiB0aGlzIGNvbHVtbg0KDQpgYGB7cn0NCmZ1bmRlZF9hbW50IDwtcmF3X2RhdGEkZnVuZGVkX2FtbnQNCmBgYA0KDQpWaWV3IHRoZSBkYXRhDQpgYGB7cn0NCg0KaGVhZChteV9kYXRhJGZ1bmRlZF9hbW50KQ0KdHlwZW9mKG15X2RhdGEkZnVuZGVkX2FtbnQpDQpzdW1tYXJ5KG15X2RhdGEkZnVuZGVkX2FtbnQpDQpmaWx0ZXIobXlfZGF0YSwgaXMubmEoZnVuZGVkX2FtbnQpKSAjIG5vIE51bGwNCmZpbHRlcihteV9kYXRhLCBmdW5kZWRfYW1udCA9PSAwKSAjIG5vIFplcm8NCmZpbHRlcihteV9kYXRhLCBmdW5kZWRfYW1udCA8IDApICMgbm8gbmVnYXRpdmUNCmBgYA0KDQpTZWUgd2hlcmUgZnVuZGVkX2FtbnQgZ3JlYXRlciB0aGFuLCBsZXNzIHRoYW4sIGVxdWFsIHRvIGxvYW5fYW10DQoNClZpc3VhbGl6ZSB0aGVtIHRvZ2V0aGVyDQpgYGB7cn0NCmdncGxvdChkYXRhID0gbXlfZGF0YSkgKw0KICBnZW9tX3BvaW50KGFlcyh4ID0gbG9hbl9hbW50LCB5ID0gZnVuZGVkX2FtbnQpLCBhbHBoYSA9IDAuMDUpDQoNCmBgYA0KDQoNCiMjIyBmdW5kZWRfYW1udF9pbnYNClRoZSB0b3RhbCBhbW91bnQgY29tbWl0dGVkIGJ5IGludmVzdG9ycyBmb3IgdGhhdCBsb2FuIGF0IHRoYXQgcG9pbnQgaW4gdGltZS4NCmlzOiBjaGFyYWN0ZXINCnNob3VsZCBiZTogaW50ZWdlciAob3IgZG91YmxlPykNCg0KY2xlYXIgd29yayBkb25lIG9uIHRoaXMgY29sdW1uDQpgYGB7cn0NCmZ1bmRlZF9hbW50X2ludiA8LXJhd19kYXRhJGZ1bmRlZF9hbW50X2ludg0KDQoNCmBgYA0KDQpUdXJuIHRoZW0gaW50byBpbnRlZ2Vycw0KYGBge3J9DQpoZWFkKG15X2RhdGEkZnVuZGVkX2FtbnRfaW52KQ0KbXlfZGF0YSRmdW5kZWRfYW1udF9pbnYgPC0gYXMuaW50ZWdlcihteV9kYXRhJGZ1bmRlZF9hbW50X2ludikNCmhlYWQobXlfZGF0YSRmdW5kZWRfYW1udF9pbnYpDQpgYGANCg0KU2VlIHdoZXJlIGZ1bmRlZF9hbW50X2ludiBncmVhdGVyIHRoYW4sIGxlc3MgdGhhbiwgZXF1YWwgdG8gbG9hbl9hbXQNCmBgYHtyfQ0KZ2dwbG90KGRhdGEgPSBteV9kYXRhKSArDQogIGdlb21fcG9pbnQoYWVzKHggPSBsb2FuX2FtbnQsIHkgPSBmdW5kZWRfYW1udF9pbnYpLCBhbHBoYSA9IDAuMDUpDQpgYGANClRoaXMgc2hvd3MgdHdvIGRpZmZlcmVudCBjdXJ2ZXMsIGluZGljYXRpbmcgdHdvIGRpZmZlcmVudCB0eXBlcyBvZiBib3dlcnMgcmVjZWl2aW5nIGludmVzdG9yIGZ1bmRpbmcuIFdoYXQgaXMgdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgdHdvIHR5cGVzIG9mIGJvcnJvd2Vycz8NCg0KIyMjIHRlcm0NClRoZSBudW1iZXIgb2YgcGF5bWVudHMgb24gdGhlIGxvYW4uIFZhbHVlcyBhcmUgaW4gbW9udGhzIGFuZCBjYW4gYmUgZWl0aGVyIDM2IG9yIDYwLg0KDQoNCipOT1RFKiBHd2VuIHNheXMgdGhpcyB3aWxsIG5vdCBiZSBhdmFpbGFibGUgaW4gbmV3IGxvYW4gYXBwbGljYXRpb25zLCBidXQgdGhpcyBtYWtlcyBubyBzZW5zZSBhcyB0aGVyZSBpcyBhIGRpc3RpbmN0IGRpZmZlcmVuY2UgaW4gaW50ZXJlc3QgcmF0ZXMgZm9yIDM2IG1vbnRoIHZlcnN1cyA2MCBtb250aCBsb2Fucy4NCg0KaXM6IGNoYXJhY3RlciwgZWl0aGVyICIgMzYgbW9udGhzIiBvciAiIDYwIG1vbnRocyINCnNob3VsZCBiZTogaW50ZWdlciwgZWl0aGVyIDM2IG9yIDYwICh0byByZXByZXNlbnQgbnVtYmVyIG9mIG1vbnRocykuIEJ1dCBkb2VzIGl0IGV2ZW4gbWF0dGVyIGlmIGl0J3MgZXNzZW50aWFsbHkgY2F0ZWdvcmljYWw/DQoNClRoaXMgaXMgc2ltaWxhciB0byBhIGNhdGVnb3JpY2FsIHZhcmlhYmxlLiBQZXJoYXBzIGl0IGV4cGxhaW5zIHRoZSB0d28gY3VydmVzIG9ic2VydmVkIGluIGZ1bmRlZF9hbXRfaW52LiAobm90ZSwgbm8sIGl0IGRvZXMgbm90IGFwcGVhciB0byBhZnRlciBhbmFseXNpcykNCg0KY2xlYXIgd29yayBkb25lIG9uIHRoaXMgY29sdW1uDQpgYGB7cn0NCnRlcm0gPC1yYXdfZGF0YSR0ZXJtDQoNCmBgYA0KDQoNClVzZSB0aGlzIGlmIGtlZXBpbmcgaXQgYXMgYSBzdHJpbmcNCmBgYHtyfQ0KbXlfZGF0YSR0ZXJtIDwtIHN0cl90cmltKG15X2RhdGEkdGVybSkNCmhlYWQobXlfZGF0YSR0ZXJtKQ0KYGBgDQoNCnZpc3VhbGl6aW5nIHRoZSBkYXRhDQpgYGB7cn0NCmdncGxvdChkYXRhID0gbXlfZGF0YSwgYWVzKHggPSB0ZXJtKSkgKyANCiAgZ2VvbV9iYXIoKQ0KYGBgDQoNClRoZXJlIGFyZSBhYm91dCB0d2ljZSBhcyBtYW55IDM2IG1vbnRoIGxvYW5zIGFzIDYwIG1vbnRoLg0KDQpMZXQncyBwbG90IHRoZSBmdW5kZWRfYW1udF9pbnYgZmlsdGVyaW5nIGJ5IDM2IG1vbnRoIGFuZCA2MCBtb250aA0KYGBge3J9DQpnZ3Bsb3QoZGF0YSA9IGZpbHRlcihteV9kYXRhLCB0ZXJtID09ICIzNiBtb250aHMiKSwgbWFwcGluZyA9IGFlcyh4ID0gbG9hbl9hbW50LCB5ID0gZnVuZGVkX2FtbnRfaW52KSkgKw0KICAgIGdlb21fcG9pbnQoYWxwaGEgPSAuMDUpDQpgYGANCg0KVGhpcyBsb29rcyBhIGxvdCBsaWtlIHRoZSBvdGhlciBncmFwaCB3aXRoIG5vIGZpbHRlcmluZy4NCmBgYHtyfQ0KZ2dwbG90KGRhdGEgPSBmaWx0ZXIobXlfZGF0YSwgdGVybSA9PSAiNjAgbW9udGhzIiksIG1hcHBpbmcgPSBhZXMoeCA9IGxvYW5fYW1udCwgeSA9IGZ1bmRlZF9hbW50X2ludikpICsNCiAgICBnZW9tX3BvaW50KGFscGhhID0gLjA1KQ0KDQpgYGANClRoaXMgc2hvd3MgdGhyZWUgY3VydmVzLiANCkZpcnN0LCB0aGVyZSBpcyBhbiB1cHBlciBjdXJ2ZSB3aGVyZSBpdCBiZWdpbnMgbGluZWFybHkuDQpTZWNvbmQsIHRoZXJlIGlzIGEgZGlzY29udGludWl0eSB3aGVyZSB0aGUgc2xvcGUgZHJhc3RpY2FsbHkgY2hhbmdlcyB0b3dhcmRzIDAgKGFuZCBwb3NzaWJseSBhcHBlYXJzIHRvIGJlIHVwd2FyZCBjdXJ2aW5nPykNClRoaXJkLCB0aGVyZSBpcyB0aGUgbG93ZXIgY3VydmUsIHdoaWNoIGFwcGVhcnMgbW9yZSBsaW5lYXIgdGhhbiB0aG9zZSBmb3IgMzYgbW9udGggdGVybXMsIGJ1dCBzdGlsbCBkb3dud2FyZCBjdXJ2aW5nIG92ZXJhbGwuDQoNCg0KVmlzdWFsaXplIHRlcm0gdmVyc3VzIGxvYW4gYW1vdW50DQpgYGB7cn0NCmdncGxvdCgpICsgDQogIGdlb21fYm94cGxvdChtYXBwaW5nID0gYWVzKHggPSB0ZXJtLCB5ID0gbG9hbl9hbW50KSwgZGF0YSA9IGZpbHRlcihteV9kYXRhLCB0ZXJtID09ICI2MCBtb250aHMiKSkgKyANCiAgZ2VvbV9ib3hwbG90KG1hcHBpbmcgPSBhZXMoeCA9IHRlcm0sIHkgPSBsb2FuX2FtbnQpLCBkYXRhID0gZmlsdGVyKG15X2RhdGEsIHRlcm0gPT0gIjM2IG1vbnRocyIpKQ0KYGBgDQo2MCBtb250aCBsb2FucyBhcmUgZm9yIG1vcmUgbW9uZXkgdGhhbiAzNiBtb250aCBsb2Fucw0KDQpgYGB7cn0NCmdncGxvdCgpICsgDQogIGdlb21fYm94cGxvdChtYXBwaW5nID0gYWVzKHggPSB0ZXJtLCB5ID0gZnVuZGVkX2FtbnQpLCBkYXRhID0gZmlsdGVyKG15X2RhdGEsIHRlcm0gPT0gIjM2IG1vbnRocyIpKSArIA0KICBnZW9tX2JveHBsb3QobWFwcGluZyA9IGFlcyh4ID0gdGVybSwgeSA9IGZ1bmRlZF9hbW50KSwgZGF0YSA9IGZpbHRlcihteV9kYXRhLCB0ZXJtID09ICI2MCBtb250aHMiKSkNCmBgYA0KDQpUaGUgYmVsb3cgcHJvdmlkZXMgYSB3b3J0aGxlc3MgZ3JhcGguIE5lYXJseSBhbGwgbG9hbnMgZnVuZCByZWdhcmRsZXNzIG9mIHRlcm0uDQpgYGB7cn0NCmdncGxvdCgpICsgDQogIGdlb21fYm94cGxvdChtYXBwaW5nID0gYWVzKHggPSB0ZXJtLCB5ID0gZnVuZGVkX2FtbnQgLyBsb2FuX2FtbnQpLCBkYXRhID0gZmlsdGVyKG15X2RhdGEsIHRlcm0gPT0gIjM2IG1vbnRocyIpKSArIA0KICBnZW9tX2JveHBsb3QobWFwcGluZyA9IGFlcyh4ID0gdGVybSwgeSA9IGZ1bmRlZF9hbW50IC8gbG9hbl9hbW50KSwgZGF0YSA9IGZpbHRlcihteV9kYXRhLCB0ZXJtID09ICI2MCBtb250aHMiKSkNCmBgYA0KDQojIyMgaW50X3JhdGUNCkludGVyZXN0IFJhdGUgb24gdGhlIGxvYW4NCg0KaXM6IGNoYXJhY3Rlcg0Kc2hvdWxkIGJlOiBmbG9hdA0KDQpgYGB7cn0NCmhlYWQobXlfZGF0YSRpbnRfcmF0ZSkNCm15X2RhdGEkaW50X3JhdGUgPC0gYXMuZG91YmxlKG15X2RhdGEkaW50X3JhdGUpDQpoZWFkKG15X2RhdGEkaW50X3JhdGUpDQoNCg0KYGBgDQpgYGB7cn0NCg0KZ2dwbG90KG15X2RhdGEpICsNCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKHggPSBpbnRfcmF0ZSksIGJpbndpZHRoID0gLjEpDQpnZ3Bsb3QobXlfZGF0YSkgKw0KICBnZW9tX2hpc3RvZ3JhbShhZXMoeCA9IGludF9yYXRlKSwgYmlud2lkdGggPSAuMjUpDQpnZ3Bsb3QobXlfZGF0YSkgKw0KICBnZW9tX2hpc3RvZ3JhbShhZXMoeCA9IGludF9yYXRlKSwgYmlud2lkdGggPSAuNSkNCmdncGxvdChteV9kYXRhKSArDQogIGdlb21faGlzdG9ncmFtKGFlcyh4ID0gaW50X3JhdGUpLCBiaW53aWR0aCA9IDEpDQpgYGANCg0KSW50ZXJlc3QgcmF0ZSB2ZXJzdXMgbG9hbiBhbW91bnQNCjYwIG1vbnRoIGxvYW5zIGhhdmUgYSBoaWdoZXIgc3RhcnRpbmcgaW50ZXJlc3QgcmF0ZSwgYW5kIHRoZXJlIGFyZSB2ZXJ5IGZldyB1bmRlciAxMCwwMDAgbG9hbiBhbW91bnQNCmBgYHtyfQ0KZ2dwbG90KGRhdGEgPSBteV9kYXRhLCBtYXBwaW5nID0gYWVzKHggPSBsb2FuX2FtbnQsIHkgPSBpbnRfcmF0ZSwgY29sb3IgPSB0ZXJtKSkgKw0KICAgIGdlb21fcG9pbnQoYWxwaGEgPSAuMDUpDQpgYGANCg0KIyMjIGluc3RhbGxtZW50DQoNClRoZSBtb250aGx5IHBheW1lbnQgb3dlZCBieSB0aGUgYm9ycm93ZXIgaWYgdGhlIGxvYW4gb3JpZ2luYXRlcy4NCg0KKk5vdGUqIEd3ZW4gc2F5cyB0aGlzIHdpbGwgbm90IGJlIGF2YWlsYWJsZSBpbiB0aGUgbmV3IGRhdGEuDQpgYGB7cn0NCm15X2RhdGEgPC0gc3Vic2V0KG15X2RhdGEsIHNlbGVjdCA9IC1pbnN0YWxsbWVudCkNCmBgYA0KDQoNClRoaXMgc2hvdWxkIGJlIGRpcmVjdGx5IGNvcnJlbGF0ZWQgd2l0aCB0aGUgbG9hbiBhbW91bnQgYW5kIGludGVyZXN0IHJhdGUgYW5kIGRldGVybWluZWQgYnkgdGVybS4gSXQgbWF5IGJlIHVzZWZ1bCB0byB1c2UgdGhlc2UgdmFsdWVzIGluc3RlYWQgb2YgdGhvc2Ugd2hlbiBzYXksIGNvbXBhcmluZyB0byBpbmNvbWUsIGluIG9yZGVyIHRvIGF2b2lkIGNhbGN1bGF0aW9uLg0KDQpJczogc3RyaW5nDQpTaG91bGQgYmU6IGRvdWJsZQ0KDQpgYGB7cn0NCmhlYWQobXlfZGF0YSRpbnN0YWxsbWVudCkNCm15X2RhdGEkaW5zdGFsbG1lbnQgPC0gYXMuZG91YmxlKG15X2RhdGEkaW5zdGFsbG1lbnQpDQpoZWFkKG15X2RhdGEkaW5zdGFsbG1lbnQpDQpgYGANCg0KIyMjIGVtcF90aXRsZQ0KDQpUaGUgam9iIHRpdGxlIHN1cHBsaWVkIGJ5IHRoZSBCb3Jyb3dlciB3aGVuIGFwcGx5aW5nIGZvciB0aGUgbG9hbi4qDQoqIEVtcGxveWVyIFRpdGxlIHJlcGxhY2VzIEVtcGxveWVyIE5hbWUgZm9yIGFsbCBsb2FucyBsaXN0ZWQgYWZ0ZXIgOS8yMy8yMDEzDQoNClRoaXMgaXMgYSBzdHJpbmcgZW50ZXJlZCBieSB0aGUgdXNlciBhbmQgc2hvdWxkIGJlIGRyb3BwZWQuDQpgYGB7cn0NCm15X2RhdGEgPC0gc3Vic2V0KG15X2RhdGEsIHNlbGVjdCA9IC1lbXBfdGl0bGUpDQoNCmBgYA0KDQojIyMgZW1wX2xlbmd0aA0KDQpFbXBsb3ltZW50IGxlbmd0aCBpbiB5ZWFycy4gUG9zc2libGUgdmFsdWVzIGFyZSBiZXR3ZWVuIDAgYW5kIDEwIHdoZXJlIDAgbWVhbnMgbGVzcyB0aGFuIG9uZSB5ZWFyIGFuZCAxMCBtZWFucyB0ZW4gb3IgbW9yZSB5ZWFycy4gDQoNCklzOiBzdHJpbmcNClNob3VsZCBiZTogSW50ZWdlcg0KYGBge3J9DQpoZWFkKG15X2RhdGEkZW1wX2xlbmd0aCkNCg0KbXlfZGF0YSRlbXBfbGVuZ3RoIDwtIGFzLmludGVnZXIob3JkZXJlZChteV9kYXRhJGVtcF9sZW5ndGgsIGxldmVscyA9IGMoIjwgMSB5ZWFyIiwgIjEgeWVhciIsICIyIHllYXJzIiwgIjMgeWVhcnMiLCAiNCB5ZWFycyIsICI1IHllYXJzIiwgIjYgeWVhcnMiLCAiNyB5ZWFycyIsICI4IHllYXJzIiwgIjkgeWVhcnMiLCAiMTArIHllYXJzIikpKSAtIDENCg0KaGVhZChteV9kYXRhJGVtcF9sZW5ndGgpDQpgYGANCg0KU2VhcmNoIE5BDQpgYGB7cn0NCg0KZmlsdGVyKG15X2RhdGEsIGlzLm5hKG15X2RhdGEkZW1wX2xlbmd0aCkpDQpgYGANCk1vcmUgdGhhbiA0MCwwMDAgcm93cyBoYXZlIG5vIGVtcGxveW1lbnQgaW5mb3JtYXRpb24uIFRoYXQgc2VlbXMgbGlrZSBhIGxvdCB0byBkcm9wLiBIb3cgc2hvdWxkIHdlIGhhbmRsZSB0aGVtPw0KDQojIyBob21lX293bmVyc2hpcA0KDQpUaGUgaG9tZSBvd25lcnNoaXAgc3RhdHVzIHByb3ZpZGVkIGJ5IHRoZSBib3Jyb3dlciBkdXJpbmcgcmVnaXN0cmF0aW9uLiBPdXIgdmFsdWVzIGFyZTogUkVOVCwgT1dOLCBNT1JUR0FHRSwgT1RIRVIuDQoNCklzOiBTdHJpbmcNClNob3VsZCBiZTogUGVyaGFwcyBhbHNvIGFuIG9yZGVyZWQgc2V0IG9mIGludGVnZXJzICgiT1RIRVIiLCAiUkVOVCIsICJNT1JUR0FHRSIsICJPV04iKS4gQ291bGQgYWxzbyBtYWtlIGR1bW15IGNvbHVtbnMsIGJ1dCB0aGlzIGlzIG15IG9yZGVyIGlmIEkgd2VyZSBhc3Nlc3NpbmcgYm9ycm93ZXJzLiBXZSBzaG91bGQgY29tcGFyZSBib3RoIG1vZGVscy4NCmBgYHtyfQ0KaGVhZChteV9kYXRhJGhvbWVfb3duZXJzaGlwKQ0KDQpteV9kYXRhJGhvbWVfb3duZXJzaGlwIDwtIGFzLmludGVnZXIob3JkZXJlZChteV9kYXRhJGhvbWVfb3duZXJzaGlwLCBsZXZlbHMgPSBjKCJPVEhFUiIsICJSRU5UIiwgIk1PUlRHQUdFIiwgIk9XTiIpKSkNCmhlYWQobXlfZGF0YSRob21lX293bmVyc2hpcCkNCg0KDQpgYGANCg0KRHJvcCBudWxscy4gVGhlcmUgYXJlIHZlcnkgZmV3ICg0NykNCmBgYHtyfQ0KZmlsdGVyKG15X2RhdGEsIGlzLm5hKG15X2RhdGEkaG9tZV9vd25lcnNoaXApKQ0KbXlfZGF0YSA8LSBmaWx0ZXIobXlfZGF0YSwgISBpcy5uYShteV9kYXRhJGhvbWVfb3duZXJzaGlwKSkNCmZpbHRlcihteV9kYXRhLCBpcy5uYShteV9kYXRhJGhvbWVfb3duZXJzaGlwKSkNCmBgYA0KDQojIyMgYW5udWFsX2luYw0KVGhlIHNlbGYtcmVwb3J0ZWQgYW5udWFsIGluY29tZSBwcm92aWRlZCBieSB0aGUgYm9ycm93ZXIgZHVyaW5nIHJlZ2lzdHJhdGlvbi4NCg0KSXM6IHN0cmluZw0Kc2hvdWxkIGJlOiBJbnQNCg0KRmlsdGVyIGZvciBudWxsLCB6ZXJvLCBuZWdhdGl2ZQ0KYGBge3J9DQpmaWx0ZXIobXlfZGF0YSwgaXMubmEobXlfZGF0YSRhbm51YWxfaW5jKSkgIyBmb3VyIGl0ZW1zIGFyZSBub24tTnVsbCwgZHJvcCB0aGVtLg0KZmlsdGVyKG15X2RhdGEsIG15X2RhdGEkYW5udWFsX2luYyA9PSAwKSAjIHR3byBhcmUgemVybyBpbmNvbWUsIGJ1dCBzaWduaWZpY2FudCBpbmNvbWUgZnJvbSBqb2ludCBhcHBsaWNhbnQsIHNvIGRvbid0IGRyb3AgdGhlbS4NCmZpbHRlcihteV9kYXRhLCBteV9kYXRhJGFubnVhbF9pbmMgPCAxMDAwMCkgIyA0NjAgYXBwbGljYW50cy4NCmZpbHRlcihteV9kYXRhLCBteV9kYXRhJGFubnVhbF9pbmMgPCA1MDApICMgb25seSB0aGUgdHdvIDAgaW5jb21lIGFwcGxpY2FudHMNCmZpbHRlcihteV9kYXRhLCBteV9kYXRhJGFubnVhbF9pbmMgPCAwKSAjIGFsbCBhcmUgcG9zaXRpdmUNCmBgYA0KDQpEcm9wIE5BDQpgYGB7cn0NCm15X2RhdGEgPC0gZmlsdGVyKG15X2RhdGEsICEgaXMubmEoYW5udWFsX2luYykpDQoNCmBgYA0KDQpgYGB7cn0NCg0KaGVhZChteV9kYXRhJGFubnVhbF9pbmMpDQpteV9kYXRhJGFubnVhbF9pbmMgPC0gYXMuaW50ZWdlcihteV9kYXRhJGFubnVhbF9pbmMpDQpoZWFkKG15X2RhdGEkYW5udWFsX2luYykNCmBgYA0KYGBge3J9DQpjb3VudChmaWx0ZXIobXlfZGF0YSwgdmVyaWZpY2F0aW9uX3N0YXR1cyA9PSAwKSkNCmBgYA0KDQpgYGB7cn0NCmdncGxvdChkYXRhID0gZmlsdGVyKG15X2RhdGEsIGFubnVhbF9pbmMgPiAxMDAwMDAwKSwgYWVzKHggPSBhbm51YWxfaW5jLCB5ID0gaW50X3JhdGUpKSArDQogICAgICAgICBnZW9tX3BvaW50KGFlcyhjb2xvciA9IHZlcmlmaWNhdGlvbl9zdGF0dXMpKQ0KYGBgDQpXZSBzaG91bGQgZGl2aWRlIGluY29tZSBvdmVyICQxbWlsIGJ5IDEwMCBiZWNhdXNlIHByZXN1bWFibHkgc29tZSBvZiB0aGVzZSBwZW9wbGUgZW50ZXJlZCBkb2xsYXJzIGFuZCBjZW50cywgYnV0IHRoZSBzeXN0ZW0gZGlkIG5vdCByZWNvZ25pemUgdGhhdCB0aGV5IHdlcmUgZW50ZXJpbmcgY2VudHMuDQpgYGB7cn0NCmZpbHRlcihteV9kYXRhLCBhbm51YWxfaW5jID4gMTAwMDAwMCkNCmBgYA0KDQpOZWVkcyBhIGRlY2lzaW9uIG9uIHRoZSBsaW1pdCBvZiBpbmNvbWUsIHdoZXJlIHdlIHdpbGwgYXNzdW1lIGluY29ycmVjdCBkYXRhIGVudHJ5LiBJIGFzc3VtZSAkMSwwMDAsMDAwIHNob3VsZCBoYXZlIGJlZW4gJDEwLDAwMC4wMCwgYnV0IGl0IGNvdWxkIGJlIGhpZ2hlciBvciBsb3dlci4NCmBgYHtyfQ0KDQpteV9kYXRhJGFubnVhbF9pbmMgPC0gaWZlbHNlKG15X2RhdGEkYW5udWFsX2luYyA+PSAxMDAwMDAwLCBteV9kYXRhJGFubnVhbF9pbmMgLyAxMDAsIG15X2RhdGEkYW5udWFsX2luYykNCmZpbHRlcihteV9kYXRhLCBhbm51YWxfaW5jID4gMTAwMDAwMCkNCg0KDQpzdW1tYXJ5KG15X2RhdGEkYW5udWFsX2luYykNCmBgYA0KDQoNCiMjIyB2ZXJpZmljYXRpb25fc3RhdHVzDQoNCmlzX2luY192IGluIHRoZSBkaWN0aW9uYXJ5DQpJbmRpY2F0ZXMgaWYgaW5jb21lIHdhcyB2ZXJpZmllZCBieSBMQywgbm90IHZlcmlmaWVkLCBvciBpZiB0aGUgaW5jb21lIHNvdXJjZSB3YXMgdmVyaWZpZWQNCg0KbWFrZSBpdCBhbiBpbnRlZ2VyLCB3aXRoIG5vdCB2ZXJpZmllZCBhdCAwIGluIG9yZGVyIHRvIHNob3cgdGhlIGdhaW4gZnJvbSB2ZXJpZmllZA0KDQpgYGB7cn0NCmhlYWQobXlfZGF0YSR2ZXJpZmljYXRpb25fc3RhdHVzKQ0KbXlfZGF0YSR2ZXJpZmljYXRpb25fc3RhdHVzIDwtIGFzLmludGVnZXIoZmFjdG9yKG15X2RhdGEkdmVyaWZpY2F0aW9uX3N0YXR1cykpIC0gMQ0KaGVhZChteV9kYXRhJHZlcmlmaWNhdGlvbl9zdGF0dXMpDQoNCmBgYA0KDQpubyBOQQ0KYGBge3J9DQpmaWx0ZXIobXlfZGF0YSwgaXMubmEobXlfZGF0YSR2ZXJpZmljYXRpb25fc3RhdHVzKSkNCmBgYA0KDQpEb2VzIHZlcmlmaWNhdGlvbiBoYXZlIGEgYmVuZWZpdD8gWWVzDQpgYGB7cn0NCmxtLmZpdCA8LSBsbShpbnRfcmF0ZX52ZXJpZmljYXRpb25fc3RhdHVzLCBteV9kYXRhKQ0Kc3VtbWFyeShsbS5maXQpDQpgYGANCg0KYGBge3J9DQpnZ3Bsb3QoZGF0YSA9IGZpbHRlcihteV9kYXRhLCB2ZXJpZmljYXRpb25fc3RhdHVzID09IDApLCBhZXMoeCA9IHZlcmlmaWNhdGlvbl9zdGF0dXMsIHkgPSBpbnRfcmF0ZSkpICsNCiAgZ2VvbV9ib3hwbG90KCkNCmdncGxvdChkYXRhID0gZmlsdGVyKG15X2RhdGEsIHZlcmlmaWNhdGlvbl9zdGF0dXMgPT0gMSksIGFlcyh4ID0gdmVyaWZpY2F0aW9uX3N0YXR1cywgeSA9IGludF9yYXRlKSkgKw0KICBnZW9tX2JveHBsb3QoKQ0KZ2dwbG90KGRhdGEgPSBmaWx0ZXIobXlfZGF0YSwgdmVyaWZpY2F0aW9uX3N0YXR1cyA9PSAyKSwgYWVzKHggPSB2ZXJpZmljYXRpb25fc3RhdHVzLCB5ID0gaW50X3JhdGUpKSArDQogIGdlb21fYm94cGxvdCgpDQpgYGANClRoaXMgaXMgc3VycHJpc2luZyB0aGF0IHBlb3BsZSB3aXRoIHZlcmlmaWVkIGluY29tZSBoYXZlIGEgaGlnaGVyIGludGVyZXN0IHJhdGUgdGhhbiBwZW9wbGUgd2l0aCBhbiB1bnZlcmlmaWVkIGluY29tZS4NCg0KIyMjIGlzc3VlX2QNClRoZSBtb250aCB3aGljaCB0aGUgbG9hbiB3YXMgZnVuZGVkDQoNCipOb3RlKiBHd2VuIHNheXMgdGhpcyB3aWxsIG5vdCBiZSBpbiBuZXcgZGF0YS4NCmBgYHtyfQ0KbXlfZGF0YSA8LSBzdWJzZXQobXlfZGF0YSwgc2VsZWN0ID0gLWlzc3VlX2QpDQpgYGANCg0KSXM6IHN0cmluZw0KU2hvdWxkIGJlOiANCg0KSXMgdGhpcyB1c2VmdWw/IFByb2JhYmx5IHdvdWxkIGltYWdpbmUgdGhhdCBpbnRlcmVzdCByYXRlcyBjaGFuZ2UuDQoNCkFkZGVkIHRoZSBkYXRlIGFzIDAxIGluIG9yZGVyIHRvIGNvbnZlcnQgdG8gYSBkYXRlLg0KYGBge3J9DQpoZWFkKG15X2RhdGEkaXNzdWVfZCkNCmYgPC0gbXlfZGF0YSRpc3N1ZV9kDQoNCmZvciAobiBpbiAxOmxlbmd0aChmKSl7DQogIGZbbl0gPC0gcGFzdGUoIjAxLSIsIGZbbl0sIHNlcCA9ICIiKQ0KfQ0KbXlfZGF0YSRpc3N1ZV9kIDwtIGFzLkRhdGUoZiwgZm9ybWF0ID0gIiVkLSViLSVZIikNCmhlYWQobXlfZGF0YSRpc3N1ZV9kKQ0KDQoNCmBgYA0KDQpTZWUgaW50ZXJlc3QgcmF0ZSBvdmVyIHRpbWUuIEkgbmVlZCB0byBtYWtlIGEgbWVhbiBpbnRlcmVzdCByYXRlIGZvciBlYWNoIG1vbnRoLCBvciBtYXliZSBmaWx0ZXIgYnkgdGVybSwgdG8gbWFrZSB0aGlzIG1vcmUgaGVscGZ1bC4NCmBgYHtyfQ0KZ2dwbG90KG15X2RhdGEsIGFlcyh4ID0gaXNzdWVfZCwgeSA9IGludF9yYXRlKSkgKw0KICBnZW9tX2xpbmUoKQ0KYGBgDQoNCiMjIyBsb2FuX3N0YXR1cw0KQ3VycmVudCBzdGF0dXMgb2YgdGhlIGxvYW4NCg0KVGhpcyBpcyBmb3IgYWZ0ZXIgdGhlIGxvYW4gaXMgaXNzdWVkLCBzbyBJIGRvbid0IHRoaW5rIGl0IHdpbGwgYmUgdXNlZnVsIGluIHByZWRpY3RpbmcgaW50ZXJlc3QgcmF0ZS4NCg0KYGBge3J9DQpsZXZlbHMoZmFjdG9yKG15X2RhdGEkbG9hbl9zdGF0dXMpKQ0KYGBgDQoNClRoaXMgYXBwbGllcyBvbmx5IHRvIGN1cnJlbnQgbG9hbnMgYW5kIHNob3VsZCBiZSBkZWxldGVkDQpgYGB7cn0NCm15X2RhdGEgPC0gc3Vic2V0KG15X2RhdGEsIHNlbGVjdCA9IC1sb2FuX3N0YXR1cykNCg0KYGBgDQoNCiMjIyBweW1udF9wbGFuDQoNCkluZGljYXRlcyBpZiBhIHBheW1lbnQgcGxhbiBoYXMgYmVlbiBwdXQgaW4gcGxhY2UgZm9yIHRoZSBsb2FuDQoNClRoaXMgYXBwbGllcyBvbmx5IHRvIGN1cnJlbnQsIGRlZmF1bHRlZCBsb2FucyBhbmQgc2hvdWxkIGJlIGRlbGV0ZWQNCmBgYHtyfQ0KbXlfZGF0YSA8LSBzdWJzZXQobXlfZGF0YSwgc2VsZWN0ID0gLXB5bW50X3BsYW4pDQpgYGANCg0KIyMjIHVybA0KDQpVUkwgZm9yIHRoZSBMQyBwYWdlIHdpdGggbGlzdGluZyBkYXRhLg0KDQpUaGlzIGluZm9ybWF0aW9uIGlzIG5vdCByZWxldmFudCB0byBhbmFseXNpcyBhbmQgc2hvdWxkIGJlIGRyb3BwZWQuDQpgYGB7cn0NCm15X2RhdGEgPC0gc3Vic2V0KG15X2RhdGEsIHNlbGVjdCA9IC11cmwpDQoNCmBgYA0KDQojIyMgZGVzYw0KDQpMb2FuIGRlc2NyaXB0aW9uIHByb3ZpZGVkIGJ5IHRoZSBib3Jyb3dlcg0KDQpUaGlzIGluZm9ybWF0aW9uIGNvdWxkIGJlIGltcG9ydGFudCBidXQgd291bGQgYmUgaW1wb3NzaWJsZSB0byBhbmFseXNlIHNpbmNlIGl0IGlzIHVzZXItZW50ZXJlZC4NCmBgYHtyfQ0KbXlfZGF0YSA8LSBzdWJzZXQobXlfZGF0YSwgc2VsZWN0ID0gLWRlc2MpDQoNCmBgYA0KDQojIyMgcHVycG9zZQ0KDQpBIGNhdGVnb3J5IHByb3ZpZGVkIGJ5IHRoZSBib3Jyb3dlciBmb3IgdGhlIGxvYW4gcmVxdWVzdC4gDQoNCklzOiBTdHJpbmcNClNob3VsZCBiZTogRHVtbXkgY29sdW1ucy4NCg0KYGBge3J9DQpmaWx0ZXIobXlfZGF0YSwgaXMubmEobXlfZGF0YSRwdXJwb3NlKSkgIyBhbGwgYXJlIG5vbi1OdWxsDQpgYGANCg0KVGhlIGFuc3dlciBoZXJlIGlzIHJlbGV2YW50IGZvciB0aGUgaW50ZXJlc3QgcmF0ZS4NCmBgYHtyfQ0KbGV2ZWxzKGZhY3RvcihteV9kYXRhJHB1cnBvc2UpKQ0KZ2dwbG90KG15X2RhdGEsIGFlcyh4ID0gcHVycG9zZSwgeSA9IGludF9yYXRlKSkgKw0KICBnZW9tX2JveHBsb3QoKQ0KDQpgYGANClNob3VsZCBJIHJlbW92ZSB0aGUgZmlyc3QgZHVtbXkgdG8gZ3VhcmQgYWdhaW5zdCBtdWx0aWNvbGxpbmlhcml0eT8NCmBgYHtyfQ0KbXlfZGF0YSA8LSBkdW1teV9jb2x1bW5zKG15X2RhdGEsIHNlbGVjdF9jb2x1bW5zID0gInB1cnBvc2UiLCByZW1vdmVfc2VsZWN0ZWRfY29sdW1ucyA9IFRSVUUpDQoNCmBgYA0KDQpOZWVkIHRvIHNlZSB3aGV0aGVyIGVhY2ggY2F0ZWdvcnkgaGFzIGEgZ29vZCBudW1iZXIgb2Ygb2JzZXJ2YXRpb25zLg0KDQojIyMgdGl0bGUNCg0KVGhlIGxvYW4gdGl0bGUgcHJvdmlkZWQgYnkgdGhlIGJvcnJvd2VyDQoNCg0KVGhpcyBpbmZvcm1hdGlvbiBjb3VsZCBiZSBpbXBvcnRhbnQgYnV0IHdvdWxkIGJlIGltcG9zc2libGUgdG8gYW5hbHlzZSBzaW5jZSBpdCBpcyB1c2VyLWVudGVyZWQuDQpgYGB7cn0NCm15X2RhdGEgPC0gc3Vic2V0KG15X2RhdGEsIHNlbGVjdCA9IC10aXRsZSkNCg0KYGBgDQoNCiMjIyB6aXBfY29kZQ0KDQpUaGUgZmlyc3QgMyBudW1iZXJzIG9mIHRoZSB6aXAgY29kZSBwcm92aWRlZCBieSB0aGUgYm9ycm93ZXIgaW4gdGhlIGxvYW4gYXBwbGljYXRpb24uDQoNClRoaXMgaW5mb3JtYXRpb24gd291bGQgaW5kaWNhdGUgd2hlcmUgaW4gdGhlIGNvdW50cnkgdGhlIGJvcnJvd2VyIGlzIGxvY2F0ZWQuIEl0IG1pZ2h0IGJlIGEgcmVsZXZhbnQgdmFyaWFibGUsIGJ1dCBpdCB3b3VsZCBhdCBiZXN0IGJlIGFuIGFwcHJveGltYXRpb24gb2YgYSBib3Jyb3dlcidzIGluY29tZS4gSSBkbyBub3QgdGhpbmsgaXQgcHJvdmlkZXMgc2lnbmlmaWNhbnQgaW5mb3JtYXRpb24uDQoNCmBgYHtyfQ0KbXlfZGF0YSA8LSBzdWJzZXQobXlfZGF0YSwgc2VsZWN0ID0gLXppcF9jb2RlKQ0KDQpgYGANCg0KDQojIyMgYWRkcl9zdGF0ZQ0KDQpUaGUgc3RhdGUgcHJvdmlkZWQgYnkgdGhlIGJvcnJvd2VyIGluIHRoZSBsb2FuIGFwcGxpY2F0aW9uDQoNCg0KVGhpcyBpbmZvcm1hdGlvbiB3b3VsZCBpbmRpY2F0ZSB3aGVyZSBpbiB0aGUgY291bnRyeSB0aGUgYm9ycm93ZXIgaXMgbG9jYXRlZC4gSXQgbWlnaHQgYmUgYSByZWxldmFudCB2YXJpYWJsZSwgYnV0IGl0IHdvdWxkIGF0IGJlc3QgYmUgYW4gYXBwcm94aW1hdGlvbiBvZiBhIGJvcnJvd2VyJ3MgaW5jb21lLiBJIGRvIG5vdCB0aGluayBpdCBwcm92aWRlcyBzaWduaWZpY2FudCBpbmZvcm1hdGlvbi4NCg0KYGBge3J9DQpteV9kYXRhIDwtIHN1YnNldChteV9kYXRhLCBzZWxlY3QgPSAtYWRkcl9zdGF0ZSkNCg0KYGBgDQoNCiMjIyBkdGkNCg0KQSByYXRpbyBjYWxjdWxhdGVkIHVzaW5nIHRoZSBib3Jyb3dlcuKAmXMgdG90YWwgbW9udGhseSBkZWJ0IHBheW1lbnRzIG9uIHRoZSB0b3RhbCBkZWJ0IG9ibGlnYXRpb25zLCBleGNsdWRpbmcgbW9ydGdhZ2UgYW5kIHRoZSByZXF1ZXN0ZWQgTEMgbG9hbiwgZGl2aWRlZCBieSB0aGUgYm9ycm93ZXLigJlzIHNlbGYtcmVwb3J0ZWQgbW9udGhseSBpbmNvbWUuDQoNCklzOiBzdHJpbmcNClNob3VsZCBiZTogZG91YmxlDQoNCmBgYHtyfQ0KZmlsdGVyKG15X2RhdGEsIGlzLm5hKGR0aSkpICMgbm8gTnVsbA0KZmlsdGVyKG15X2RhdGEsIGR0aSA9PSAwKSAjIDQwMyBhcmUgemVybywgYnV0IHRoaXMgaW5kaWNhdGVzIG5vIGRlYnQuIFNlZW1zIG9kZCB0aGV5IHR1cm4gdG8gTGVuZGluZyBDbHViIGZvciBhIGxvYW4gaWYgdGhpcyBpcyB0cnVlLg0KZmlsdGVyKG15X2RhdGEsIGR0aSA8IDEpICMgMzI2NiBhcmUgYmVsb3cgdGhpcyByYXRpbw0KZmlsdGVyKG15X2RhdGEsIGR0aSA8IDApICMgbm8gbmVnYXRpdmUNCmBgYA0KDQpgYGB7cn0NCmhlYWQobXlfZGF0YSRkdGkpDQpmIDwtIGFzLmRvdWJsZShteV9kYXRhJGR0aSkNCmhlYWQoZikNCmBgYA0KDQoNCiMjIyBkZWxpbnFfMnlycw0KDQpUaGUgbnVtYmVyIG9mIDMwKyBkYXlzIHBhc3QtZHVlIGluY2lkZW5jZXMgb2YgZGVsaW5xdWVuY3kgaW4gdGhlIGJvcnJvd2VyJ3MgY3JlZGl0IGZpbGUgZm9yIHRoZSBwYXN0IDIgeWVhcnMNCg0KSXM6IEludGVnZXINClNob3VsZCBiZTogSW50ZWdlcg0KYGBge3J9DQp0eXBlb2YobXlfZGF0YSRkZWxpbnFfMnlycykNCnN1bW1hcnkobXlfZGF0YSRkZWxpbnFfMnlycykNCmBgYA0KDQpUaGVyZSBhcmUgMjEgTkFzLiBEcm9wIHRoZW0uDQpgYGB7cn0NCm15X2RhdGEgPC0gZmlsdGVyKG15X2RhdGEsICEgaXMubmEobXlfZGF0YSRkZWxpbnFfMnlycykpDQpzdW1tYXJ5KG15X2RhdGEkZGVsaW5xXzJ5cnMpDQoNCmBgYA0KDQoNCiMjIyBlYXJsaWVzdF9jcl9saW5lDQoNClRoZSBtb250aCB0aGUgYm9ycm93ZXIncyBlYXJsaWVzdCByZXBvcnRlZCBjcmVkaXQgbGluZSB3YXMgb3BlbmVkDQoNCmBgYHtyfQ0KZmlsdGVyKG15X2RhdGEsIGlzLm5hKGVhcmxpZXN0X2NyX2xpbmUpKSAjIG5vIE5BDQpgYGANCg0KDQpUaGlzIGNvdWxkIGJlIGEgcGxhY2Vob2xkZXIgZm9yIHRoZSBib3Jyb3dlcidzIGFnZS4gSXMgaXQgY29ycmVsYXRlZCB3aXRoIGludGVyZXN0IHJhdGU/IEkgd291bGQgaW1hZ2luZSB0aGF0IGlmIHRoaXMgZGF0ZSBpcyBvbGQsIHRoZSBwZXJzb24gaXMgb2xkLCBzbyBoYXMgYSBoaWdoZXIgaW50ZXJlc3QgcmF0ZSwgYW5kIGlmIHlvdW5nLCBsb3cgaW5jb21lLCBzbyBoaWdoZXIgaW50ZXJlc3QgcmF0ZS4gQnV0IHRoaXMgd291bGQgc2ltcGx5IGJlIGJlY2F1c2Ugb2YgbXkgZXhwZWN0YXRpb25zIG9mIGluY29tZSBieSBhZ2UsIGFuZCB0aGlzIGRhdGEgZG9lcyBub3QgZGlyZWN0bHkgdGVsbCB1cyBhbnl0aGluZy4NCg0KQWRkZWQgdGhlIGRhdGUgYXMgMDEgaW4gb3JkZXIgdG8gY29udmVydCB0byBhIGRhdGUuDQpgYGB7cn0NCmhlYWQobXlfZGF0YSRlYXJsaWVzdF9jcl9saW5lKQ0KZiA8LSBteV9kYXRhJGVhcmxpZXN0X2NyX2xpbmUNCg0KZm9yIChuIGluIDE6bGVuZ3RoKGYpKXsNCiAgZltuXSA8LSBwYXN0ZSgiMDEtIiwgZltuXSwgc2VwID0gIiIpDQp9DQpteV9kYXRhJGVhcmxpZXN0X2NyX2xpbmUgPC0gYXMuRGF0ZShmLCBmb3JtYXQgPSAiJWQtJWItJVkiKQ0KaGVhZChteV9kYXRhJGVhcmxpZXN0X2NyX2xpbmUpDQoNCg0KYGBgDQoNCkRyb3AgdGhlIGRhdGEgYXMgaXQgaXMgbm90IHJlYWxseSB1c2VmdWwuDQpgYGB7cn0NCm15X2RhdGEgPC0gc3Vic2V0KG15X2RhdGEsIHNlbGVjdCA9IC1lYXJsaWVzdF9jcl9saW5lKQ0KDQpgYGANCg0KIyMjIGlucV9sYXN0XzZtdGhzDQoNClRoZSBudW1iZXIgb2YgaW5xdWlyaWVzIGluIHBhc3QgNiBtb250aHMgKGV4Y2x1ZGluZyBhdXRvIGFuZCBtb3J0Z2FnZSBpbnF1aXJpZXMpDQoNClRoaXMgcmVwcmVzZW50cyBob3cgbWFueSB0aW1lcyBhIGxlbmRlciBoYXMgcHVsbGVkIHRoZSBib3Jyb3dlcidzIGNyZWRpdCByZXBvcnQuIEluIGl0c2VsZiBpdCBkb2VzIG5vdCBtZWFuIGFueXRoaW5nLCBidXQgYSBoaWdoZXIgdmFsdWUgaXMgdGFrZW4gYXMgYW4gaW5kaWNhdGlvbiB0aGF0IHRoZSBib3Jyb3dlciBpcyBub3QgY3JlZGl0d29ydGh5IChlbHNlLCB3aHkgYXNrIGZvciBzbyBtdWNoIGRlYnQ/KSAwLTIgaXMgZmluZS4NCg0KVGhlcmUgYXJlIG5vIE5BIHZhbHVlcw0KDQpgYGB7cn0NCmhlYWQobXlfZGF0YSRpbnFfbGFzdF82bXRocykNCmNvdW50KG15X2RhdGEsIGlzLm5hKGlucV9sYXN0XzZtdGhzKSkNCmBgYA0KUGxvdCB0aGUgcmFuZ2UNCmBgYHtyfQ0KZ2dwbG90KGRhdGEgPSBteV9kYXRhLCBhZXMoeCA9IGlucV9sYXN0XzZtdGhzKSkgKw0KICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDEpDQpgYGANClBvd2VyIGxhdyBkaXN0cmlidXRpb24uDQoNCmBgYHtyfQ0KbG0uMSA8LSBsbShpbnRfcmF0ZX5pbnFfbGFzdF82bXRocywgbXlfZGF0YSkNCnN1bW1hcnkobG0uMSkNCmBgYA0KVGhpcyBoYXMgYSBzaWduaWZpY2FudCBpbXBhY3Qgb24gaW50ZXJlc3QgcmF0ZS4NCg0KIyMjIG10aHNfc2luY2VfbGFzdF9kZWxpbnENCg0KVGhlIG51bWJlciBvZiBtb250aHMgc2luY2UgdGhlIGJvcnJvd2VyJ3MgbGFzdCBkZWxpbnF1ZW5jeS4NCg0KVGhpcyBpcyB0aGUgdGltZSB0aGF0IGhhcyBwYXNzZWQgc2luY2UgYSBidXllciB3YXMgZGVsaW5xdWVudC4gQXBwcm94aW1hdGVseSBoYWxmIG9mIHRoZSBlbnRyaWVzIGFyZSBOQS4gKkhPV0VWRVIqIGlmIHRoZSBib3Jyb3dlciBoYXMgbmV2ZXIgYmVlbiBkZWxpbnF1ZW50ICh0aGF0IGlzLCBhbHdheXMgcGF5cyBvbiB0aW1lKSwgaXQgKlNIT1VMRCogYmUgTkEuIE5BIGlzIGEgYmV0dGVyIHZhbHVlIHRoYW4gaGF2aW5nIGFueXRoaW5nIGhlcmUuICpCVVQqIHRoZSBkYXRhIHNob3dzIHRoaXMgaXMgbm90IGFjdHVhbGx5IHRydWUsIGFuZCB0aG9zZSB3aXRoIE5BIGhhdmUgYSBoaWdoZXIgaW50ZXJlc3QgcmF0ZS4NCg0KUG9zc2libGUgc29sdXRpb25zOiB2ZXJ5LCB2ZXJ5IGhpZ2ggdmFsdWUgdG8gcmVwbGFjZSBOQT8NCg0KYGBge3J9DQpsZXZlbHMoZmFjdG9yKG15X2RhdGEkbXRoc19zaW5jZV9sYXN0X2RlbGlucSkpDQpjb3VudChteV9kYXRhLCBpcy5uYShtdGhzX3NpbmNlX2xhc3RfZGVsaW5xKSkNCmBgYA0KYGBge3J9DQpnZ3Bsb3QoZGF0YSA9IG15X2RhdGEsIGFlcyh4ID0gbXRoc19zaW5jZV9sYXN0X2RlbGlucSwgeSA9IGludF9yYXRlKSkgKw0KICBnZW9tX3BvaW50KGFscGhhID0gMC4wNSkNCmBgYA0KVGhlcmUgaXMgYSBoYXJkIGN1dG9mZiBhdCA4NCBtb250aHMgYmVjYXVzZSB0aG9zZSBkZWxpbnF1ZW5jaWVzIGFyZSBubyBsb25nZXIgcmVwb3J0ZWQgb24gYSBjcmVkaXQgcmVwb3J0ICg3IHllYXJzKS4gVGhlcmUgYXJlIHN0aWxsIHNvbWUgb2JzZXJ2YXRpb25zIGJleW9uZCB0aGF0IHRpbWUgcGVyaW9kLCBidXQgSSBhbSBub3Qgc3VyZSB3aHkuIFRoZXkgbWF5IHByZS1kYXRlIHRoZSBjaGFuZ2UgaW4gdGhlIGxhdyAoSSByZWNhbGwgdGhpcyB3YXMgc29tZXRpbWUgYXJvdW5kIDIwMDktMjAxMCkgb3IgbG9vcGhvbGVzIHRoYXQgYWxsb3cgY29udGludWluZyByZXBvcnRpbmcsIG9yIHNlbGYtcmVwb3J0aW5nIHRvIExlbmRpbmdDbHViLg0KDQpXZSBuZWVkIHRvIGZpZ3VyZSBvdXQgYSB3YXkgdG8gZGVhbCB3aXRoIHRoZSA8ODQsID49ODQgcHJvYmxlbS4NCg0KYGBge3J9DQpjb3VudChteV9kYXRhLCBtdGhzX3NpbmNlX2xhc3RfZGVsaW5xID09IDgzKQ0KY291bnQobXlfZGF0YSwgbXRoc19zaW5jZV9sYXN0X2RlbGlucSA9PSA4NCkNCg0KDQpgYGANCg0KVGhvc2Ugd2l0aCBhbiBOQSBoYXZlIGEgaGlnaGVyIGludGVyZXN0IHJhdGUsIHdoaWNoIEkgd291bGQgbm90IGV4cGVjdC4NCmBgYHtyfQ0KZ2dwbG90KGRhdGEgPSBteV9kYXRhKSArDQogIGdlb21fYm94cGxvdChtYXBwaW5nID0gYWVzKHggPSBpcy5uYShtdGhzX3NpbmNlX2xhc3RfZGVsaW5xKSwgeSA9IGludF9yYXRlKSkNCmBgYA0KDQoNCiMjIyBtdGhzX3NpbmNlX2xhc3RfcmVjb3JkDQoNClRoZSBudW1iZXIgb2YgbW9udGhzIHNpbmNlIHRoZSBsYXN0IHB1YmxpYyByZWNvcmQuDQoNClRoaXMgd291bGQgYmUgdGhlIG1vbnRocyBzaW5jZSB0aGUgbGFzdCBwdWJsaWMgcmVjb3JkIGZpbGluZywgd2hpY2ggbWVhbnMgYSBsYXdzdWl0IHRvIGNvbGxlY3QgYSBkZWJ0Lg0KRnJvbSBteSBvbGQgY3JlZGl0IHJlcG9ydCBmcm9tIDIwMTU6ICJQdWJsaWMgcmVjb3JkIGluZm9ybWF0aW9uIGluY2x1ZGVzIGJhbmtydXB0Y2llcywgbGllbnMgb3IganVkZ21lbnRzIGFuZCBjb21lcyBmcm9tIGZlZGVyYWwsIHN0YXRlIG9yIGNvdW50eSBjb3VydCByZWNvcmRzLiIgVG9kYXksIHRoZXkgaW5jbHVkZSBvbmx5IGJhbmtydXB0Y2llcywgYnV0IHRoaXMgaXMgbm90IHJlbGV2YW50Lg0KDQpUaGlzIGlzIHZlcnksIHZlcnkgYmFkLCBhbmQgbXVjaCBiZXR0ZXIgaWYgaXQgaXMgTkEgKGkuZS4gdGhlcmUgYXJlIG5vbmUpDQoNCmBgYHtyfQ0KbGV2ZWxzKGZhY3RvcihteV9kYXRhJG10aHNfc2luY2VfbGFzdF9yZWNvcmQpKQ0KY291bnQobXlfZGF0YSwgaXMubmEobXRoc19zaW5jZV9sYXN0X3JlY29yZCkpDQpgYGANCg0KYGBge3J9DQpnZ3Bsb3QoZGF0YSA9IG15X2RhdGEsIGFlcyh4ID0gbXRoc19zaW5jZV9sYXN0X3JlY29yZCwgeSA9IGludF9yYXRlKSkgKw0KICBnZW9tX3BvaW50KGFscGhhID0gMC4wNSkNCmBgYA0KSSBkbyBub3Qgc2VlIGEgcGF0dGVybiBhcHBlYXIuDQoNClRoZXkgYWxzbyByZW1haW4gb24geW91ciBjcmVkaXQgcmVwb3J0IGZvciB0ZW4geWVhcnMgZHVyaW5nIHRoaXMgdGltZSBwZXJpb2QgKHRoaXMgaXMgbm8gbG9uZ2VyIHRydWUsIGJ1dCBub3QgcmVsZXZhbnQpDQoNCg0KYGBge3J9DQpjb3VudChteV9kYXRhLCBtdGhzX3NpbmNlX2xhc3RfcmVjb3JkID09IDExOSkNCmNvdW50KG15X2RhdGEsIG10aHNfc2luY2VfbGFzdF9yZWNvcmQgPT0gMTIwKQ0KDQpgYGANCg0KVGhvcyB3aXRoIG5vIHB1YmxpYyByZWNvcmRzIGhhdmUgaGlnaGVyIGludGVyZXN0IHJhdGVzIHRoYW4gdGhvc2Ugd2hvIGRvLCB3aGljaCBJIHdvdWxkIG5vdCBleHBlY3QuDQoNCmBgYHtyfQ0KZ2dwbG90KGRhdGEgPSBteV9kYXRhKSArDQogIGdlb21fYm94cGxvdChtYXBwaW5nID0gYWVzKHggPSBpcy5uYShtdGhzX3NpbmNlX2xhc3RfcmVjb3JkKSwgeSA9IGludF9yYXRlKSkNCmBgYA0KDQojIyMgb3Blbl9hY2MNCg0KVGhlIG51bWJlciBvZiBvcGVuIGNyZWRpdCBsaW5lcyBpbiB0aGUgYm9ycm93ZXIncyBjcmVkaXQgZmlsZS4NCg0KSXM6IEludGVnZXINClNob3VsZCBiZTogSW50ZWdlcg0KDQpJIHdvdWxkIGV4cGVjdCB0aGlzIHRvIGJlIGdlbmVyYWxseSBvbiB0aGUgaGlnaCBzaWRlIGZvciB1c2VycyBvZiB0aGlzIHBsYXRmb3JtLg0KDQoNCmBgYHtyfQ0KdHlwZW9mKG15X2RhdGEkb3Blbl9hY2MpDQpsZXZlbHMoZmFjdG9yKG15X2RhdGEkb3Blbl9hY2MpKQ0KY291bnQobXlfZGF0YSwgaXMubmEob3Blbl9hY2MpKSAjIG5vIE5BDQpgYGANCg0KYGBge3J9DQpnZ3Bsb3QoZGF0YSA9IG15X2RhdGEsIGFlcyh4ID0gb3Blbl9hY2MpKSArDQogIGdlb21faGlzdG9ncmFtKCkNCg0Kc3VtbWFyeShteV9kYXRhJG9wZW5fYWNjKQ0KYGBgDQpJcyB0aGlzIGFzc29jaWF0ZWQgd2l0aCBpbnRfcmF0ZT8NCmBgYHtyfQ0KbG0uMiA8LSBsbShpbnRfcmF0ZX5vcGVuX2FjYywgbXlfZGF0YSkNCnN1bW1hcnkobG0uMikNCmBgYA0KVGhpcyBzYXlzIHRoZSBpbnRlcmVzdCByYXRlIGdvZXMgZG93biBmb3IgcGVvcGxlIHdpdGggbW9yZSBvcGVuIGFjY291bnRzIGFuZCB0aGF0IHRoaXMgaXMgc2lnbmlmaWNhbnQsIHdoaWNoIHNlZW1zIG9kZC4gVGhlIHJlbGF0aW9uc2hpcCBpcyBwcm9iYWJseSBub24tbGluZWFyLCBpZiBzaWduaWZpY2FudC4NCg0KIyMjIHB1Yl9yZWMNCg0KTnVtYmVyIG9mIGRlcm9nYXRvcnkgcHVibGljIHJlY29yZHMNCg0KSXM6IEludGVnZXINClNob3VsZCBiZTogSW50ZWdlcg0KDQpgYGB7cn0NCnR5cGVvZihteV9kYXRhJHB1Yl9yZWMpDQpjb3VudChteV9kYXRhLCBpcy5uYShwdWJfcmVjKSkgIyBObyBOQQ0KYGBgDQpIaXN0b2dyYW0gc2hvd3MgdmVyeSwgdmVyeSBzdHJvbmdseSBwb3dlciBsYXcuDQpgYGB7cn0NCmdncGxvdChkYXRhID0gbXlfZGF0YSwgYWVzKHggPSBwdWJfcmVjKSkgKw0KICBnZW9tX2hpc3RvZ3JhbSgpDQpzdW1tYXJ5KG15X2RhdGEkcHViX3JlYykNCmBgYA0KDQojIyMgcmV2b2xfYmFsYW5jZQ0KDQpUb3RhbCBjcmVkaXQgcmV2b2x2aW5nIGJhbGFuY2UNCg0KVGhpcyBpcyB0aGUgcmV2b2x2aW5nIGRlYnQgKGNyZWRpdCBjYXJkLCB1c3VhbGx5KSBvZiB0aGUgYm9ycm93ZXIuIEl0IHdpbGwgYmUgYSBwYXJ0IG9mIHRoZSBkdGkgY2FsY3VsYXRpb24uDQoNCmBgYHtyfQ0KaGVhZChteV9kYXRhJHJldm9sX2JhbCkNCmNvdW50KG15X2RhdGEsIGlzLm5hKHJldm9sX2JhbCkpDQpmaWx0ZXIobXlfZGF0YSwgaXMubmEocmV2b2xfYmFsKSkNCmNvdW50KG15X2RhdGEsIHJldm9sX2JhbCA9PSAwKQ0KYGBgDQoNClR3byBhcmUgbmEuIE9uZSBoYXMgYSByZXZvbF91dGlsIGZpZ3VyZSwgc28gSSBleHBlY3QgdGhpcyBpcyBhIGRhdGEgZXJyb3IuIFRoZSBvdGhlciBoYXMgYSByZXZvbF91dGlsIG9mIDAsIHNvIHRoaXMgbWF5IGJlIHNob3dpbmcgYSByZXZvbF9iYWwgPT0gMC4gU2luY2UgaXQgaXMgb25seSB0d28gb2JzZXJ2YXRpb25zLCB3ZSBzaG91bGQgZHJvcCB0aGVtLg0KDQpgYGB7cn0NCmZpbHRlcihteV9kYXRhLCBpcy5uYShteV9kYXRhJHJldm9sX2JhbCkpDQpteV9kYXRhIDwtIGZpbHRlcihteV9kYXRhLCAhIGlzLm5hKG15X2RhdGEkcmV2b2xfYmFsKSkNCmZpbHRlcihteV9kYXRhLCBpcy5uYShteV9kYXRhJHJldm9sX2JhbCkpDQpgYGANCg0KIyMjIHJldm9sX3V0aWwNCg0KUmV2b2x2aW5nIGxpbmUgdXRpbGl6YXRpb24gcmF0ZSwgb3IgdGhlIGFtb3VudCBvZiBjcmVkaXQgdGhlIGJvcnJvd2VyIGlzIHVzaW5nIHJlbGF0aXZlIHRvIGFsbCBhdmFpbGFibGUgcmV2b2x2aW5nIGNyZWRpdC4NCg0KVGhpcyBpcyBhIHJhdGlvIHNob3dpbmcgaG93IG1heGVkIG91dCB0aGUgYm9ycm93ZXIncyBjcmVkaXQgY2FyZHMgYXJlLiBXZSB3b3VsZCBleHBlY3QgY3JlZGl0d29ydGh5IGJvcnJvd2VycyB0byBoYXZlIGEgbG93IHJhdGlvLg0KDQpJczogc3RyaW5nDQpTaG91bGQgYmU6IGRvdWJsZQ0KDQpgYGB7cn0NCnR5cGVvZihteV9kYXRhJHJldm9sX3V0aWwpDQpjb3VudChteV9kYXRhLCBpcy5uYShyZXZvbF91dGlsKSkgIyA0MjkgYXJlIE5BLiBEbyB3ZSBkcm9wIHRoZW0/IElmIHdlIHVzZSB0aGlzIHZhcmlhYmxlLCB0aGVuIHByb2JhYmx5LiBCdXQgaXMgdGhpcyB2YXJpYWJsZSBwcmVkaWN0aXZlIGVub3VnaCB0byB1c2U/IExpa2VseSBub3QuDQpteV9kYXRhJHJldm9sX3V0aWwgPC0gYXMuZG91YmxlKG15X2RhdGEkcmV2b2xfdXRpbCkNCmhlYWQobXlfZGF0YSRyZXZvbF91dGlsKQ0KDQpgYGANCg0KYGBge3J9DQpnZ3Bsb3QoZGF0YSA9IG15X2RhdGEsIGFlcyh4ID0gcmV2b2xfdXRpbCwgeSA9IGludF9yYXRlKSkgKw0KICBnZW9tX3BvaW50KGFscGhhID0gMC4wNSkNCmBgYA0KDQpIYXMgYSBmYWlybHkgbm9ybWFsIGRpc3RyaWJ1dGlvbi4NCmBgYHtyfQ0KZ2dwbG90KGRhdGEgPSBteV9kYXRhLCBhZXMoeCA9IHJldm9sX3V0aWwpKSArDQogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMSkNCmBgYA0KDQojIyMgdG90YWxfYWNjDQoNClRoZSB0b3RhbCBudW1iZXIgb2YgY3JlZGl0IGxpbmVzIGN1cnJlbnRseSBpbiB0aGUgYm9ycm93ZXIncyBjcmVkaXQgZmlsZQ0KDQpJIGRvIG5vdCBleHBlY3QgdGhpcyB0byBiZSB1c2VmdWwgdG8gdGhlIGFuYWx5c2lzLiBJdCBkb2VzIG5vdCBkaXN0aW5ndWlzaCBiZXR3ZWVuIG9wZW4gb3IgY2xvc2VkIGFjY291bnRzLiBJbiBnZW5lcmFsIG5vdCBhIGdvb2QgcHJlZGljdG9yLg0KDQpJczogaW50ZWdlcg0KU2hvdWxkIGJlOiBpbnRlZ2VyDQoNCmBgYHtyfQ0KaGVhZChteV9kYXRhJHRvdGFsX2FjYykNCmBgYA0KIyMjIGluaXRpYWxfbGlzdF9zdGF0dXMNCg0KVGhlIGluaXRpYWwgbGlzdGluZyBzdGF0dXMgb2YgdGhlIGxvYW4uIFBvc3NpYmxlIHZhbHVlcyBhcmUg4oCTIFcsIEYNCg0KIkxlbmRpbmcgQ2x1YiByZXNlcnZlcyBhIGZldyBsb2FucyBmb3IgMTIgaG91cnMgYW5kIG9mZmVycyB0aGVtIHRvIHRoZSBpbnN0aXR1dGlvbmFsIGFuZCBsYXJnZSByZXRhaWwgbGVuZGVycyB3aG8gd2FudCB0byBsZW5kIHRoZSB3aG9sZSBhbW91bnQgZm9yIGEgbG9hbi4gSSBhbSBub3Qgc3VyZSB3aGV0aGVyIHRoZSBoaXN0b3JpY2FsIGxvYW4gZGF0YSBmaWxlIGluY2x1ZGVzIHRoZSBsb2FucyB0aGF0IHdlcmUgb2ZmZXJlZCBhbmQgcGlja2VkIHVwIGJ5IGxlbmRlcnMgYXMgJ3dob2xlJyBsb2Fucy4gQnV0LCB0aGUgbG9hbnMgdGhhdCB3ZXJlIGluaXRpYWxseSBvZmZlcmVkIGFzIHdob2xlLCBkZXNpZ25hdGVkIHdpdGggJ3cnLCBidXQgbm90IHBpY2tlZCB1cCBhcyAnd2hvbGUnIGxvYW5zIGFyZSBsaXN0ZWQgaW4gaGlzdG9yaWNhbCBsb2FuIGRhdGEgZmlsZS4iDQpodHRwczovL2FuZGlyb2cuYmxvZ3Nwb3QuY29tLzIwMTMvMDQvbGVuZGluZy1jbHViLWJvcnJvd2Vycy1pbmNvbWUuaHRtbA0KDQpUaGlzIG1heSBiZSB1c2VmdWwgYmVjYXVzZSB0aG9zZSBsb2FucyB0YWtlbiBieSBpbnN0aXR1dGlvbmFsIGFuZCBsYXJnZSBsZW5kZXJzIG1heSBiZSB0aGUgbW9zdCBwcmVmZXJyZWQgbG9hbnMgYnkgbGVuZGVycy4NCmBgYHtyfQ0KaGVhZChteV9kYXRhJGluaXRpYWxfbGlzdF9zdGF0dXMpDQpgYGANCklzIHRoZXJlIGEgZGlmZmVyZW5jZT8NCg0KYGBge3J9DQpnZ3Bsb3QoZGF0YSA9IG15X2RhdGEsIGFlcyh4ID0gaW5pdGlhbF9saXN0X3N0YXR1cywgeSA9IGludF9yYXRlKSkgKw0KICBnZW9tX2JveHBsb3QoKQ0KYGBgDQp3IGxvYW5zIHRlbmQgdG8gaGF2ZSBhIGxvd2VyIGludGVyZXN0IHJhdGUuDQoNCk1ha2UgaXQgYW4gaW50ZWdlciwgd2l0aCBmID09IDAgYW5kIHcgPT0gMSwgYnV0IHRoaXMgY291bGQgaW5zdGVhZCBiZSBkdW1teSBjb2x1bW5zDQoNCmBgYHtyfQ0KaGVhZChteV9kYXRhJGluaXRpYWxfbGlzdF9zdGF0dXMpDQpteV9kYXRhJGluaXRpYWxfbGlzdF9zdGF0dXMgPC0gYXMuaW50ZWdlcihmYWN0b3IobXlfZGF0YSRpbml0aWFsX2xpc3Rfc3RhdHVzKSkgLSAxDQpoZWFkKG15X2RhdGEkaW5pdGlhbF9saXN0X3N0YXR1cykNCg0KYGBgDQpJcyB0aGlzIHVzZWZ1bD8NCmBgYHtyfQ0KbG0uMyA8LSBsbShpbnRfcmF0ZX5pbml0aWFsX2xpc3Rfc3RhdHVzLCBteV9kYXRhKQ0Kc3VtbWFyeShsbS4zKSAjIFJeMiBvZiAwLjAxLCBzbyBub3QgYSBodWdlIGNvbnRyaWJ1dG9yLCBidXQgdGhlIHZhbHVlcyBzaG93IGl0IGlzIHNpZ25pZmljYW50Lg0KYGBgDQojIyMgb3V0X3BybmNwDQoNClJlbWFpbmluZyBvdXRzdGFuZGluZyBwcmluY2lwYWwgZm9yIHRvdGFsIGFtb3VudCBmdW5kZWQNCg0KVGhpcyBpcyBvbmx5IHJlbGV2YW50IGFmdGVyIGEgbG9hbiBpcyBpc3N1ZWQgYW5kIHNob3VsZCBiZSBkcm9wcGVkLg0KYGBge3J9DQpteV9kYXRhIDwtIHN1YnNldChteV9kYXRhLCBzZWxlY3QgPSAtb3V0X3BybmNwKQ0KDQpgYGANCg0KIyMjIG91dF9wcm5jcF9pbnYNCg0KUmVtYWluaW5nIG91dHN0YW5kaW5nIHByaW5jaXBhbCBmb3IgcG9ydGlvbiBvZiB0b3RhbCBhbW91bnQgZnVuZGVkIGJ5IGludmVzdG9ycw0KDQpUaGlzIGlzIG9ubHkgcmVsZXZhbnQgYWZ0ZXIgYSBsb2FuIGlzIGlzc3VlZCBhbmQgc2hvdWxkIGJlIGRyb3BwZWQuDQpgYGB7cn0NCm15X2RhdGEgPC0gc3Vic2V0KG15X2RhdGEsIHNlbGVjdCA9IC1vdXRfcHJuY3BfaW52KQ0KDQpgYGANCg0KIyMjIHRvdGFsX3B5bW50DQoNClBheW1lbnRzIHJlY2VpdmVkIHRvIGRhdGUgZm9yIHRvdGFsIGFtb3VudCBmdW5kZWQNCg0KDQpUaGlzIGlzIG9ubHkgcmVsZXZhbnQgYWZ0ZXIgYSBsb2FuIGlzIGlzc3VlZCBhbmQgc2hvdWxkIGJlIGRyb3BwZWQuDQpgYGB7cn0NCm15X2RhdGEgPC0gc3Vic2V0KG15X2RhdGEsIHNlbGVjdCA9IC10b3RhbF9weW1udCkNCg0KYGBgDQoNCiMjIyB0b3RhbF9weW1udF9pbnYNCg0KUGF5bWVudHMgcmVjZWl2ZWQgdG8gZGF0ZSBmb3IgcG9ydGlvbiBvZiB0b3RhbCBhbW91bnQgZnVuZGVkIGJ5IGludmVzdG9ycw0KDQoNClRoaXMgaXMgb25seSByZWxldmFudCBhZnRlciBhIGxvYW4gaXMgaXNzdWVkIGFuZCBzaG91bGQgYmUgZHJvcHBlZC4NCmBgYHtyfQ0KbXlfZGF0YSA8LSBzdWJzZXQobXlfZGF0YSwgc2VsZWN0ID0gLXRvdGFsX3B5bW50X2ludikNCg0KYGBgDQoNCg0KYGBge3J9DQoNCg0KaGVhZChteV9kYXRhJGR0aV9qb2ludCkNCmYgPC0gYXMuZG91YmxlKG15X2RhdGEkZHRpX2pvaW50KQ0Kc3VtbWFyeShmKQ0KYGBgDQoNCg==